• 多线程


    多线程

    进程是执行程序的一次执行过程,是系统资源分配的单位;一个进程可以包含若干个线程,线程是CPU调度和执行的单位;

    一.继承Thread类

    • 自定义线程类继承Thread类
    • 重写run()方法,编写线程执行体
    • 创建线程对象,调用start()方法启动线程
    package oop;
    //线程开启不一定执行,由CPU调度执行
    public class Test2 extends Thread{
        @Override
        public void run() {
            for(int i=0;i<10;i++)
            System.out.println("看什么呢");
        }
    
        public static void main(String[] args) {
            Test2 test2=new Test2();
            test2.start();
            for (int i=0;i<1000;i++){
                System.out.println("我在吃"+i);
            }
        }
    }
    
    

    网图下载

    package oop;
    
    import org.apache.commons.io.FileUtils;
    
    import java.io.File;
    import java.io.IOException;
    import java.net.URL;
    
    //实现多线程同步下载图片
    public class Test2 extends Thread{
        private String url;
        private String name;
    
        public Test2(String url,String name){
            this.name=name;
            this.url=url;
        }
        @Override
        public void run() {
            WebDownloader webDownloader=new WebDownloader();
            webDownloader.downloader(url,name);
            System.out.println("下载的文件名围殴"+name);
        }
    
        class WebDownloader{
        public void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url), new File(name));
            }catch(IOException e){
                e.printStackTrace();
            }
        }
        }
    
        public static void main(String[] args) {
            Test2 test1=new Test2("https://t8.baidu.com/1.jpg","1.jpg");
            Test2 test2=new Test2("https://t8.baidu.com/2.jpg","2.jpg");
            Test2 test3=new Test2("https://t8.baidu.com/3.jpg","3.jpg");
            test1.start();
            test2.start();
            test3.start();
        }
    }
    
    
    

    二.实现Runable接口

    创建线程方法2:实现runable接口,重写run方法,执行线程需要丢入runable接口实现类,调用start方法

    package oop;
    public class Test2 implements Runnable{
        @Override
        public void run() {
            for(int i=0;i<10;i++)
                System.out.println("看什么呢");
        }
    
        public static void main(String[] args) {
           //创建runnable接口的实现类对象
            Test2 test2=new Test2();
            Thread thread=new Thread(test2);
            thread.start();
            for(int i=0;i<10;i++){
                System.out.println("我在学习!");
            }
            }
    }
    

    三.继承Thread类和实现Runnable接口对比

    • 继承Thread类
      • 子类继承Thread类具备多线程能力
      • 启动线程:子类对象.start()
      • 不建议使用:单继承局限性
    • 实现Runnable接口
      • 实现接口Runnable具有多线程能力
      • 启动线程:传入目标对象+Thread对象.start()
      • 可以避免单继承局限性,灵活,方便同一对象被多个线程使用
        多线程同时使用实现Runnable接口对象
    package oop;
    public class Test2 implements Runnable {
        private int ticketNums = 10;
    
        @Override
        public void run() {
            while (true) {
                if (ticketNums <= 0) {
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "拿到了底" + ticketNums + "票");
                 ticketNums--;
            }
        }
    
        public static void main(String[] args) {
            //创建runnable接口的实现类对象
            Test2 test2 = new Test2();
            new Thread(test2, "ab").start();
            new Thread(test2, "cd").start();
            new Thread(test2, "ef").start();
            new Thread(test2, "hi").start();
    
        }
    }
    

    四.静态代理

    真实对象和代理对象都要实现同一个接口
    代理对象要代理真实角色

    package oop;
    public class Test2{
        public static void main(String[] args) {
            WeddingCompany weddingCompany=new WeddingCompany(new You());
            weddingCompany.HappyMarry();
        }
    
    }
    interface Marry{
        void HappyMarry();
    }
    class You implements Marry{
        @Override
        public void HappyMarry() {
            System.out.println("结婚了");
        }
    }
    class WeddingCompany implements Marry{
        private You target;
        public  WeddingCompany(You target){
            this.target=target;
        }
    
        @Override
        public void HappyMarry() {
            before();
            this.target.HappyMarry();
            after();
        }
        private void before(){
            System.out.println("结婚前");
        }
        private void after(){
            System.out.println("结婚后");
        }
    }
    
    

    代理的好处:代理对象可以做很多真实对象做不了的事情
    真实对象能专注做直接的事情

    五.lambda表达式

    函数式接口:任何接口如果只包含一个抽象方法,那么他就是一个函数式接口;对于函数式接口,可以通过lambda表达式来创建该接口的对象

    package oop;
    public class Test2{
        public static void main(String[] args) {
            //形式1.
            Like like1= (int a)->{ System.out.println("我爱你"); };
            //形式2.
            Like like2=(a)->{
                System.out.println("你忙吧");
            };
            Like like3=a-> System.out.println("hhh");
           like1.lambda(1);
           like2.lambda(1);
           like3.lambda(1);
        }
    }
    interface Like{
        void lambda(int a);
    }
    
    

    六.线程停止

    通过外部标志位和外部停止方法进行停止

    package oop;
    public class Test2 implements Runnable{
        private boolean flag=true;
        @Override
        public void run() {
            int i=0;
            while(flag){
                System.out.println("Run"+i++);
            }
        }
        public void stop(){
            this.flag=false;
        }
    
        public static void main(String[] args) {
            Test2 test2=new Test2();
            new Thread(test2).start();
            for(int i=0;i<100;i++){
                System.out.println("main"+i);
                if(i==90){
                    System.out.println("线程该停止了");
                    test2.stop();
                }
            }
        }
    }
    

    七.线程休眠

    • sleep(时间)指定当前线程阻塞的毫秒数;1000毫秒=1秒
    • sleep存在异常InterruptedException
    • sleep时间达到后线程进入就绪状态
    • 可以模拟倒计时和网络延时
    • 每个对象都有锁,sleep不会释放锁

    八.线程礼让

    • 礼让线程,让当前正在执行的线程暂停,单不阻塞
    • 将线程从运行状态转为就绪状态
    • 让cpu重新调度,礼让不一定成功,看CPU调度
    package oop;
    public class Test2{
        public static void main(String[] args) {
            MyYield myYield=new MyYield();
            new Thread(myYield,"dwx").start();
            new Thread(myYield,"zzz").start();
        }
    }
    class MyYield implements Runnable{
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()+"线程开始执行");
            Thread.yield();//礼让
            System.out.println(Thread.currentThread().getName()+"线程停止执行");
        }
    }
    
    

    线程状态

    • NEW:尚未启动的线程处于此状态
    • RUNNABLE:在Java虚拟机中执行的线程处于此状态
    • BLOCKED:被阻塞等待监视器锁定的线程处于此状态
    • WAITING:正在等待另一个线程执行特定动作的线程处于此状态
    • TIMED_WAITING:正在等待另一个线程执行动作达到指定等待时间的线程处于此状态
    • TERMINATED:已退出的线程处于此状态
    package oop;
    public class Test2{
        public static void main(String[] args) {
            Thread thread=new Thread(()->{
                for(int i=0;i<5;i++){
                    try{
                        Thread.sleep(1000);
                    }catch(InterruptedException e){
                        e.printStackTrace();
                    }
                }
                System.out.println("------");
            });
            Thread.State state=thread.getState();
            System.out.println(state);
            thread.start();
            state=thread.getState();
            System.out.println(state);
            while(state!=Thread.State.TERMINATED){
                state=thread.getState();
                System.out.println(state);
            }
            state=thread.getState();
            System.out.println(state);
        }
    }
    

    十.线程的优先级

    • Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定调度哪个线程来执行
    • 线程的优先级用数字表示,范围从1-10
      - Thread.MIN_PRIORITY=1;
      - Thread.MAX_PRIORITY=10;
      - Thread.NORM_PRIORITY=5;
      改变或获取优先级
      getPriority(); setPriority(int XXX)
      设置在start()之前

    十一.守护线程

    • 线程分为用户线程和守护线程
    • 虚拟机必须确保用户线程执行完毕
    • 虚拟机不用等待守护线程执行完毕
    • 守护线程有后台记录操作日志,监控内存,垃圾回收等
    package oop;
    public class Test2{
        public static void main(String[] args) {
            God god =new God();
            You you =new You();
            Thread thread=new Thread(god);
            thread.setDaemon(true);//默认为false,普通线程,设置为true后为守护线程。
            thread.start();
            new Thread(you).start();
    
    
        }
    }
    class God implements Runnable{
        @Override
        public void run() {
            while(true){
                System.out.println("上帝保佑你");
            }
        }
    }
    class You implements  Runnable{
        @Override
        public void run() {
            for(int i=1;i<365;i++){
                System.out.println("这是我生命中的第"+i+"天");
            }
        }
    }
    
    
    

    十二.线程同步

    并发:同一个对象被多个线程同时操作
    线程同步:其实就是一种等待机制,多个需要同时访问此对象的线程进入这个对象的等待池形成队列,等待前面线程使用完毕,下一个线程再使用。

    同步方法和同步块

    给方法加上synchronized关键字即可,synchronized方法控制对对象的访问,每个对象对应一把锁,每个synchronized方法都必须获得调用该方法的对象的锁才能执行,否则线程会阻塞,同步块就是synchronized(obj){},要同步就是要锁变化的量

    package oop;
    
    import java.util.ArrayList;
    import java.util.List;
    
    //不安全的买票
    public class UnsafeList{
        public static void main(String[] args) {
            List<String> list=new ArrayList<>();
            for(int i=0;i<1000;i++){
                new Thread(()->{
                    synchronized(list){
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
            try {
                Thread.sleep(100);
            }catch(InterruptedException e){
                e.printStackTrace();
            }
            System.out.println(list.size());
        }
    }
    
    

    死锁

    产生死锁的四个必要条件:

    • 互斥条件:一个资源每次只能被一个进程使用
    • 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放
    • 不剥夺条件:进程已获得的资源在为使用完之前,不能强行剥夺
    • 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系

    只要破解其中的任意一个或多个条件就能避免死锁

    Lock(锁)

    java.util.concurrent.locks.Lock接口,ReentrantLock类实现了Lock,可以显式加锁释放锁

     ReentrantLock lock=new ReentrantLock();
            lock.lock();//加锁
            lock.unlock();//解锁
    

    十三.线程通信

    java提供了几个方法解决线程之间的通信问题

    • wait():表示线程一直等待,知道其他线程通知,与sleep不同,会释放锁
    • wait(long timeout):指定等待的毫秒数
    • notify():唤醒一个处于等待状态的线程
    • notifyAll():唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度

    解决生产者消费者问题

    两种解决方法:

    1. 管程法:生产者将生产好的数据放入缓冲区,消费者从缓冲区拿出数据
    2. 信号灯法:增加标志位,用标志位来通知生产者和消费者谁唤醒谁等待

    十四.线程池

    提前创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中,可以避免频繁创建销毁,实现重复利用

    优点:

    • 提高响应速度
    • 降低资源消耗
    • 便于线程管理
    package oop;
    import java.util.concurrent.ExecutorService;
    import java.util.concurrent.Executors;
    public class TestPool{
        public static void main(String[] args) {
            //创建线程池
            ExecutorService service= Executors.newFixedThreadPool(10);
            //执行
            service.execute(new MyThread());
            //关闭连接
            service.shutdown();
        }
    
    }
    class  MyThread implements Runnable{
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                System.out.println(Thread.currentThread().getName()+i);
            }
        }
    }
    
  • 相关阅读:
    djangoadmin实现文件上传下载
    Apscheduler详解(转)
    django集成Apscheduler3
    springboot实现token鉴权
    xss攻击入门
    回顾2012——运维工作周年祭
    9个常用iptables配置实例
    ardunio 实验:超声波测距、声光报警模拟倒车雷达
    一款我用了好多年的多线程FTP软件
    线程Thread基础学习(2)
  • 原文地址:https://www.cnblogs.com/python-road/p/13220870.html
Copyright © 2020-2023  润新知