多线程

线程和进程

进程:程序的一次执行 线程:进程执行的一段程序片段

main 主线程 一个进程当中会有多个线程

线程创建

Thread class -> 继承Thread类

不建议使用 比偏OOP单继承局限性

  1. 自定线程类继承Thread类
  2. 重写run() 方法,编写线程执行体
  3. 创建线程对象,调用start()方法启动线程
//创建线程方式1:继承thread类-》重写run()方法,调用start开启线程
public class Thread extends java.lang.Thread {
    static int a=1;
    @Override
    public void run(){
        for (int i=a;i<=200;i++){
            System.out.println("我在看代码---"+i);
        }
    }
    //main主线程
    public static void main(String[] args) {
        //创建一个线程对象
        Thread thread = new Thread();
        //调用 start方法
        thread.start();
        for (int i=a;i<=200;i++){
            System.out.println("我在学习多线程---"+i);
        }
    }
}

线程开启不一定立即执行 由cpu调度执行

Runnable接口 -> 实现Runable接口(推荐使用)

  1. 继承Runnable接口
  2. 重写run()方法
  3. 创建Thread对象,传入目标对象+Thread对象.start
public class Thread2 implements Runnable {
    @Override
    public void run() {
        for (int i=0;i<20;i++){
            System.out.println("线程学习"+i);
        }
    }

    public static void main(String[] args) {
        Thread2 thread2 = new Thread2();
        //创建线程对象,通过线程对象来开启线程,代理
        Thread thread = new Thread(thread2);
        thread.start();

        for (int i=0;i<200;i++){
            System.out.println("主函数"+i);
        }
    }
}

Callable接口 -> 实现Callable接口(知道即可)

public class Thread5 implements Callable {
    private String name;
    private String url;

    public Thread5(String url,String name) {
        this.name=name;
        this.url=url;
    }

    public Thread5() {
    }

    @Override
    public Boolean call() throws Exception {
        WebDownloader webDownloader = new WebDownloader();
        webDownloader.downloader(url,name);
        System.out.println("下载的文件名:"+name);
        System.out.println(url);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Thread5 t1 = new Thread5("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242332225H9-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1661401277&t=716f8fad1045378c8e5a3ce019db25d6","1.jpg");
        Thread5 t2 = new Thread5("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242332225H9-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1661401277&t=716f8fad1045378c8e5a3ce019db25d6","2.jpg");
        Thread5 t3 = new Thread5("https://gimg2.baidu.com/image_search/src=http%3A%2F%2Fimg.jj20.com%2Fup%2Fallimg%2F4k%2Fs%2F02%2F2109242332225H9-0-lp.jpg&refer=http%3A%2F%2Fimg.jj20.com&app=2002&size=f9999,10000&q=a80&n=0&g=0n&fmt=auto?sec=1661401277&t=716f8fad1045378c8e5a3ce019db25d6","3.jpg");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);
        //获取结果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
        //输出结果
        System.out.println(rs1);
        System.out.println(rs2);
        System.out.println(rs3);
        //关闭服务
        ser.shutdownNow();

    }

    class WebDownloader{
        public void downloader(String url,String name){
            try {
                FileUtils.copyURLToFile(new URL(url),new File(name));
            }catch (IOException e){
                e.printStackTrace();
                System.out.println("IO异常");
            }
        }
    }
}

多线程引用例子

//发现问题:多个线程操作同一个资源的情况下,线程不安全,数据紊乱
public class Thread4Demo implements Runnable {
    private int tickNums = 10;
    @Override
    public void run(){
        while(true){
            if (tickNums<=0){
                break;
            }
            System.out.println(Thread.currentThread().getName()+"拿到了第"+tickNums--+"票");
        }
    }
    public static void main(String[] args) {
        Thread4Demo ticket = new Thread4Demo();
        new Thread(ticket,"小明").start();
        new Thread(ticket,"老师").start();
        new Thread(ticket,"黄牛").start();
    }
}

利用多线程模拟归途赛跑

//模拟龟兔赛跑
public class Race implements Runnable{
    private static String winner;
    @Override
    public void run() {
        //判断比赛是否结束
        for (int i=0;i<=100;i++){
        //模拟褥子休息
        if (Thread.currentThread().getName().equals("兔子")&&i%10==0){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        boolean falg = gameOver(i);
        if (falg){
            break;
        }
            System.out.println(Thread.currentThread().getName()+"跑了"+i+"步");
        }
    }
    private boolean gameOver(int steps){
        if (winner!=null){
            return true;
        }if (steps==100){
            winner = Thread.currentThread().getName();
            System.out.println("winner is "+winner);
            return true;
        }
        return false;
    }
    public static void main(String[] args) {
        Race race = new Race();
        new Thread(race,"乌龟").start();
        new Thread(race,"兔子").start();
    }
}

proxy代理

分为静态代理和动态代理 静态代理总结: 真实对象和代理对象都要实现同一个接口 代理对象要代理真实角色 好处 代理对象可以在真实对象执行方法的基础上进行额外操作 真实对象只用专注做自己的事情 静态代理例子:

public class StaticProxy {
    public static void main(String[] args) {
        WeddingCompany weddingCompany = new WeddingCompany(new you("xxx"));
        new Thread( ()-> System.out.println("i love you ")).start();
        weddingCompany.HappyMarry();
    }
}
interface  Marry{
    void HappyMarry();
}
class you implements Marry{
    private String name;

    public you(String name) {
        this.name = name;
    }

    @Override
    public void HappyMarry() {
        System.out.println(name+"要结婚");
    }
}
class WeddingCompany implements Marry{
    private Marry target;
    public WeddingCompany(Marry target){
        this.target=target;
    }

    @Override
    public void HappyMarry() {
        before();
        this.target.HappyMarry();//真实对象的调用方法
        after();
    }

    private void after() {
        System.out.println("结婚之后");
    }

    private void before() {
        System.out.println("结婚之前");
    }
}

Lambda表达式

函数式编程 Runnable就是一个函数式接口,故可以使用Lambda表达式 使用条件: 必须是函数式接口 函数式接口:函数式接口是只包含一个方法的接口 作用: 避免内部类定义过多 去掉一部分没有意义的代码,留下代码核心

public class LambdaTest {
    //3.静态内部类
    static class Like2 implements ILike{
        @Override
        public void lambda() {
            System.out.println("i like lambda2");
        }
    }

    public static void main(String[] args) {
        ILike iLike = new Like();
        iLike.lambda();

        iLike = new Like2();
        iLike.lambda();

        //4.局部内部类
        class Like3 implements ILike{
            @Override
            public void lambda() {
                System.out.println("i like lambda3");
            }
        }
        iLike = new Like3();
        iLike.lambda();

        //5.匿名内部类,没有类的名称,必须借助接口或者父类
        iLike=new ILike() {
            @Override
            public void lambda() {
                System.out.println("i like lambda4");
            }
        };
        iLike.lambda();

        //6.用lambda简化
        iLike = ()-> {
            System.out.println("i like lambda5");
        };
        iLike.lambda();
        

    }
}
//1.定义一个函数接口
interface ILike{
    void lambda();
}
//2.实现类
class Like implements ILike{
    @Override
    public void lambda() {
        System.out.println("i like lambda");
    }
}

线程状态

线程停止

建议线程正常停止-->利用次数,不建议死循环 建议使用标志位-->设置一个标志位(flag=false) 不建议使用stop()或者destroy()等过时或者JDK不建议使用的方法

public class TestStop implements Runnable{
    //1.设置一个标识位
    private boolean flag = true;
    @Override
    public void run() {
        int i = 0;
        while (flag){
            System.out.println("run...Thrad"+i++);
        }
    }
    //2.设置一个公开的方法停止线程,转换标识位
    public void myStop(){
        this.flag = false;
    }

    public static void main(String[] args) {
        TestStop testStop = new TestStop();
        new Thread(testStop).start();
        for (int i = 0; i < 100; i++) {
            System.out.println("main"+i);
            if (i==90){
                testStop.myStop();
                System.out.println("线程改停止了");
            }
        }
    }
}

线程休眠

sleep制定当前线程阻塞的毫秒数 sleep存在异常InterruptException; sleep时间达到后线程进入就绪状态 sleep可以模拟网络岩石,倒计时 每个对象都有一个锁,sleep不会释放锁

//模拟倒计时
public class TestSleep2 {
    public static void main(String[] args) throws InterruptedException {
//        turnDown();
        //打印当前系统时间
        Date startTime;//获取系统当前时间
        while(true){
            Thread.sleep(1000);
            startTime = new Date(System.currentTimeMillis());
            System.out.println(new SimpleDateFormat("HH:mm:ss").format(startTime));
        }
    }
    public static void turnDown() throws InterruptedException{
        int num=10;
        while (true){
            Thread.sleep(1000);
            System.out.println(num--);
            if (num<=0){
                break;
            }
        }
    }
}

线程礼让

//礼让不一定成功
public class TestYield {
    public static void main(String[] args) {
        MyYield myYield = new MyYield();
        new Thread(myYield,"a线程").start();
        new Thread(myYield,"b线程").start();
    }
}

class MyYield implements Runnable{
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName()+"线程开始时间");
       Thread.yield();//线程礼让
        System.out.println(Thread.currentThread().getName()+"线程停止执行");
    }
}

join线程合并

理解为插队

public class TestJoin implements Runnable {

    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println("vip来了"+i);
        }
    }

    public static void main(String[] args) throws InterruptedException {
        //我们的线程
        TestJoin testJoin = new TestJoin();
        Thread thread = new Thread(testJoin);
        thread.start();

        //主线程
        for (int i = 0; i < 300; i++) {
            if (i==1){
                thread.join();
            }
            System.out.println("main"+i);
        }
    }
}

线程状态观测

  • NEW 尚未启动的线程,刚新建的线程
  • RUNNABLE 在java虚拟机中执行的线程处于此状态
  • BLOCKED 被阻塞等待监视器锁定的线程处于此状态
  • WAITING 正在等待另一个线程执行的特定动作的线程处于此状态
  • TIMED_WAITING 正在等待另一个线程执行动作达到制定等待时间的线程很粗与此状态
  • TERMINATED 已退出的线程处于此状态

线程优先级

先设置优先级,再start 默认为5 thread.setPriority(10) 最小1--最大10

守护(daemon)线程

  • 线程分为用户线程和守护线程
  • 虚拟机必须确保用户线程执行完毕
  • 虚拟机不用等待守护线程执行完毕 如后台记录操作日志,监控内存,垃圾回收等(gc线程) 主线程执行完毕,守护线程自动关闭
public class TestDeamon {
    public static void main(String[] args) {
        God god = new God();
        You you = new You();

        Thread thread = new Thread(god);
        thread.setDaemon(true);
        thread.start();
        new Thread(you).start();

    }
}
class God implements Runnable{
    @Override
    public void run() {
        while(true){
            System.out.println("god bless you");
        }
    }
}
class You implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i <= 100; i++) {
            System.out.println("Hello world "+ i +" 年");
            if (i==100){
                System.out.println("Good by world"+i);
            }
        }
    }
}

线程同步

相当于排队 形成条件:队列+锁(synchronized) 在synchronized代码块中放入想要锁的对象(也就是需要增删改的对象) 以上述第三个为例

public class UnsafeList {
    public static void main(String[] args) throws InterruptedException {
        List<String> list = new ArrayList<>();

            for (int i = 0; i < 10000; i++) {
                new Thread(()->{
                    synchronized(list){
                        list.add(Thread.currentThread().getName());
                    }
                }).start();
            }
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(list.size());
    }
}

JUC

JUC是什么? JUC是java.util.concurrent包的简称,在Java5.0添加,目的就是为了更好的支持高并发任务。让开发者进行多线程编程时减少竞争条件和死锁的问题! 并发与并行的区别: 并发(多线程操作同一个资源,交替执行) CPU一核, 模拟出来多条线程,天下武功,唯快不破,快速交替 并行(多个人一起行走, 同时进行) CPU多核,多个线程同时进行 ; 使用线程池操作

死锁

死锁是指两个或两个以上的进程在执行过程中,由于竞争资源或者由于彼此通信而造成的一种阻塞的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程。(多个线程互相抱着对方需要的资源) 产生死锁的四个必要条件

  1. 互斥条件:一个资源每次被一个进程使用
  2. 请求与保持条件:一个京城因请求资源而阻塞式,对一伙的的资源保持不妨
  3. 不剥夺条件:进程一伙的的资源,在位使用完之前,不能进行剥夺
  4. 循环等待条件:如干进程之间形成一种头尾详解的循环等待资源关系
public class DeadLock {
    public static void main(String[] args) {
        MakeUp makeUp1 = new MakeUp(0);
        MakeUp makeUp2 = new MakeUp(1);
        new Thread(makeUp1).start();
        new Thread(makeUp2).start();
    }
}
class Mirror{
}
class Lipsteak{
}
class MakeUp extends Thread{
    static Lipsteak lipsteak = new Lipsteak();
    static Mirror mirror = new Mirror();
    int choice;
    public MakeUp(int choice){
        this.choice=choice;
    }
    @Override
    public void run(){
        if (choice==0){
            synchronized (lipsteak){
                System.out.println(choice+"获取lipsteak锁");
                synchronized (mirror){
                    System.out.println(choice+"获取mirror锁");
                }
            }
        }
        else{
            synchronized (mirror){
                System.out.println(choice+"获取mirror锁");
                synchronized (lipsteak){
                    System.out.println(choice+"获取lipsteak锁");
                }
            }
        }
    }
}

锁(lock)

jdk5.0开始 同步锁使用Lock对象

lock 和 synchronized 区别:

  1. synchronized:在需要同步的对象中加入此控制,synchronized可以加在方法上,也可以加在特定代码块中,括号中表示需要锁的对象。 lock:一般使用ReentrantLock类做为锁。在加锁和解锁处需要通过lock()和unlock()显示指出。所以一般会在finally块中写unlock()以防死锁。
  2. synchronized是托管给JVM执行的, 而lock是java写的控制锁的代码。
public class TestLock {
    public static void main(String[] args) {
        TestLock2 testLock2 = new TestLock2();

        new Thread(testLock2,"1").start();
        new Thread(testLock2,"2").start();
    }
}
class TestLock2 extends Thread {
    int tickNum=10;
    private final ReentrantLock lock = new ReentrantLock();
    @Override
    public void run() {
        while(true) {
            try {
                lock.lock();
                if (tickNum>0){
                    try {
                        Thread.sleep(1000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(tickNum--);
                }else{
                    break;
                }
            }finally {
                lock.unlock();
            }
        }
    }
}

线程协作

  1. 管程法-->利用缓冲区解决
public class TestPC {
    public static void main(String[] args) {
        SynContainer container = new SynContainer();

        new Productor(container).start();
        new Consumer(container).start();
    }
}
//生产者
class Productor extends Thread{
    SynContainer container;
    public Productor(SynContainer container){
        this.container=container;
    }
    //生产
    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            System.out.println("生产了"+i+"个产品");
            container.push(new Product(i));
        }
    }
}
//消费者
class Consumer extends Thread{
    SynContainer container;
    public Consumer(SynContainer container){
        this.container=container;
    }
    //消费者消费
    @Override
    public void run(){
        for (int i = 0; i < 100; i++) {
            try {
                System.out.println("消费了"+container.pop().id+"个产品");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
//产品
class Product{
    int id;//产品编号
    public Product(int id) {
        this.id = id;
    }
}
//缓冲区
class SynContainer{
    //容器大小
    Product[] products = new Product[10];
    //容器计数器
    int count=0;

    //生产者放入产品
    public synchronized void push(Product product){
        //如果容器满了,需要等待消费者消费
        if (count==products.length){
            //通知消费者消费,生产者等待
            try{
                this.wait();
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        //如果没有满,我们需要丢入产品
        products[count]=product;
        count++;

        //通知消费着消费
        this.notifyAll();
    }
    //消费者消耗产品
    public synchronized Product pop() throws InterruptedException {
        if (count==0){
            //等待生产者生产,消费者等待
            this.wait();
        }
        //如果可以消费
        count--;
        Product product = products[count];

        //使用完了 通知生产者生产
        this.notifyAll();
        return product;
    }
}


  1. 信号灯法 用标识解决
  public class TestPC2 {
   public static void main(String[] args) {
       TV tv = new TV();

       new Player(tv).start();
       new Watcher(tv).start();
   }
}
//生产者  演员
class Player extends Thread{
   TV tv;
   public Player(TV tv){
       this.tv = tv;
   }
   @Override
   public void run(){
       for (int i = 0; i < 20; i++) {
           if (i%2==0){
               this.tv.play("快乐大本营播放中");
           }else{
               this.tv.play("抖音记录美好生活");
           }
       }
   }
}
//消费者  观众
class Watcher extends Thread{
   TV tv;
   public Watcher(TV tv){
       this.tv = tv;
   }

   @Override
   public void run(){
       for (int i = 0; i < 20; i++) {
               this.tv.watch();
       }
   }
}
//产品  节目
class TV{
   //演员表演,观众等待 T
   //观众观看,演员等待 F
   String voice;
   boolean flag = true;

   //表演
   public synchronized void play(String voice)  {
       if (!flag){
           try {
               this.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       System.out.println("演员表演了:"+voice);
       //通知观众观看
       this.notifyAll();//唤醒wait
       this.voice=voice;
       this.flag=!this.flag;
   }
   //观看
   public synchronized void watch() {
       if (flag){
           try {
               this.wait();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
       System.out.println("观看了:"+voice);
       this.flag=true;
       this.notifyAll();
   }
}

线程池

思路:提前差创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁 实现重复利用。类似交通工具 corePoolSize 线程池大小 maximumPoolSize:最大线程书 keepAliveTime:线程没有任务时最多保持多长时间会停止 JDK5.0起提供了线程池相关的API:Executors和ExecutorService ExecutorService:真正的线程池接口。常见的子类ThreadPoolExecutor Executors:工具类 线程池的工厂类 用于创建并返回不同类型的线程池

public class TestPool {
    public static void main(String[] args) {
        //1.创建服务,创建线程池
        ExecutorService service = Executors.newFixedThreadPool(10);

        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        service.execute(new MyThread());
        //2.关闭连接
        service.shutdown();
    }
}
class MyThread implements Runnable{
    @Override
    public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName());
        }
    }
}


本文章使用limfx的vscode插件快速发布