线程通讯

1.为什么要线程通信

多个线程并发执行时,在默认情况下cpu是随机切换线程的,有时我们希望cpu按我们的规律执行
线程,此时就需要线程之间协调通信。

2.Object和Condition休眠唤醒方式

  • Object的wait、notify、notifyAull
  • Condition的await、signal、singalAll

Object和Condition休眠唤醒方式的区别

  • object wait()必须在synchronized(同步锁)下使用
  • Object wait()必须要通过nodify()方法进行唤醒
  • condition await()必须和Lock(互斥锁/共享锁)配合使用
  • condition await()必须通过signal()方法进行唤醒

2.1 Object的(wait、notify)的等待唤醒实现奇数偶数打印

public class OddEven {

    private Integer num = 0;
    private final Object obj = new Object();
    /***
     * 奇数打印方法,由奇数线程调用
     */
    public  void odd(){
        synchronized (obj){
            while (num<10){
                if(num % 2 != 0){
                    System.out.println("奇数:num="+num);
                    num++;
                    obj.notify();//唤醒其他线程方法,唤醒偶数线程打印
                }else {
                    try {
                        obj.wait();//等待偶数线程打印完毕
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

    /**
     * 偶数打印方法,由偶数线程调用
     */
    public void even(){
        synchronized (obj){
            while (num<10){
                if(num % 2 == 0){
                    System.out.println("偶数:num="+num);
                    num++;
                    obj.notify();//唤醒其他线程方法,唤醒奇数线程打印
                }else {
                    try {
                        obj.wait();//等待奇数线程打印完毕
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }

    }

    public static void main(String[] args) {
        final OddEven oddEven = new OddEven();
        //1.开始奇数线程打印
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                oddEven.odd();
            }
        });
        //2.开启偶数线程打印
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                oddEven.even();
            }
        });
        thread1.start();
        thread2.start();
    }
}

2.2 Condition的等待唤醒(await、signal)实现奇数偶数打印

public class OddEven {


    private Integer num = 0;
    private Lock lock = new ReentrantLock();//注意不要设置ture
    private Condition condition = lock.newCondition();
    /***
     * 奇数打印方法,由奇数线程调用
     */
    public  void odd(){
            while (num<10){
                lock.lock();
                try {
                    if(num % 2 != 0){
                        System.out.println("奇数:num="+num);
                        num++;
                        condition.signal();//唤醒其他线程方法,唤醒偶数线程打印
                    }else {
                        try {
                            condition.await();//等待偶数线程打印完毕
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                } finally {
                    lock.unlock();
                }
            }

    }

    /**
     * 偶数打印方法,由偶数线程调用
     */
    public void even(){

        while (num<10){
            lock.lock();
            try {
                if(num % 2 == 0){
                    System.out.println("偶数:num="+num);
                    num++;
                    condition.signal();//唤醒其他线程方法,唤醒奇数线程打印
                }else {
                    try {
                        condition.await();//等待奇数线程打印完毕
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        final OddEven oddEven = new OddEven();
        //1.开始奇数线程打印
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                oddEven.odd();
            }
        });
        //2.开启偶数线程打印
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                oddEven.even();
            }
        });
        thread1.start();
        thread2.start();
    }


}

3.CountDownLatch方式

  • CountDownLatch是在java1.5被引入的,存在于java.util。concurrent包下。
  • CountDownLatch能够使一个线程等待其他线程完成各自的工作后在执行
  • CountDownLatch是通过一个计数器来实现的,计数器的初始值为线程的数量
    在这里插入图片描述
    CountDownLatch类创建时,会指定等待线程数量初始值,如图所示,等待三个线程,
    要等待的线程是TA会调用await()方法,await()方法会判断当前值是否为0(等待线程数量初始值)
    会进行阻塞,T1/T2/T3会分别启动,调用countDown()方法,会将计数器的初始值进行
    减一操作,当计数器的值到0时,表示所有线程已经完成了任务,然后TA开始执行。

栗子:教练等待运动员到齐训练

public class CountDownLatchDemo {

    private CountDownLatch countDownLatch = new CountDownLatch(3);//设置要等待的线程数量
    /***
     * 运动员方法
     */
    public void racer(){
        //1.获取运动员线程名称
        String name = Thread.currentThread().getName();
        //2.运动员开始准备
        System.out.println(name+"运动员正在准备......");
        try {
            //3.准备过程
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("运动员准备完毕!");
        countDownLatch.countDown();
    }

    /***
     * 教练方法
     */
    public void coach(){
        //1.获取教练线程名称
        String name = Thread.currentThread().getName();
        //2.教练等待所有运动员准备完毕:打印等待信息
        System.out.println(name+"教练等待所有运动员准备......");
        //3.调用CountDownLatch的await()方法等待其他线程执行完毕
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //4.所有运动员已就绪,教练开始训练,打印训练信息
        System.out.println("所有运动员已就绪"+name+"开始训练!");
    }

    public static void main(String[] args) {
        //1.创建CountDownLatchDemo实例
       final CountDownLatchDemo countDownLatchDemo = new CountDownLatchDemo();
        //2.创建三个运动员
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                countDownLatchDemo.racer();
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                countDownLatchDemo.racer();
            }
        });
        Thread thread3 = new Thread(new Runnable() {
            public void run() {
                countDownLatchDemo.racer();
            }
        });
        //3.创建教练
        Thread thread4 = new Thread(new Runnable() {
            public void run() {
                countDownLatchDemo.coach();
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();
    }

}
打印结果:
Thread-0运动员正在准备......
Thread-1运动员正在准备......
Thread-2运动员正在准备......
Thread-3教练等待所有运动员准备......
运动员准备完毕!
运动员准备完毕!
运动员准备完毕!
所有运动员已就绪Thread-3开始训练!

4.CyclicBarrier

  • CyclicBarrier是在java1.5被引入的,存在于java.util.concurrent包下。
  • CyclicBarrier实现让一组线程等待至某个状态之后再全部同时执行。
  • CyclicBarrier底层是基于ReentrantLock和Condition实现

三个线程同时启动案例:

public class CyclicBarrierDemo {

    private CyclicBarrier cyclicBarrier = new CyclicBarrier(3);//3代表三个线程同时启动
    public void startThread(){
        //1.打印线程准备启动
        String name = Thread.currentThread().getName();
        System.out.println(name+"正在准备......");
        //2.调用cyclicBarrier的await()方法
        try {
            cyclicBarrier.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
        //3.打印线程启动完毕信息
        System.out.println(name+"已经启动完毕:"+new Date().getTime());
    }

    public static void main(String[] args) {
        final CyclicBarrierDemo cyclicBarrierDemo = new CyclicBarrierDemo();
        Thread thread1 = new Thread(new Runnable() {
            public void run() {
                cyclicBarrierDemo.startThread();
            }
        });

        Thread thread2 = new Thread(new Runnable() {
            public void run() {
                cyclicBarrierDemo.startThread();
            }
        });

        Thread thread3 = new Thread(new Runnable() {
            public void run() {
                cyclicBarrierDemo.startThread();
            }
        });
        thread1.start();
        thread2.start();
        thread3.start();
    }
}

打印结果:
Thread-0正在准备......
Thread-1正在准备......
Thread-2正在准备......
Thread-2已经启动完毕:1569399711592
Thread-0已经启动完毕:1569399711592
Thread-1已经启动完毕:1569399711592

5.Semaphore方式

Semaphore在java1.5被引入的,存在于java.util.concurrent包下,
用于控制对某组资源的访问权限

演示:工人使用机器工作8个工人使用3台机器工作,机器为互斥资源(即每次只能一个人使用)

public class SemaphoreDemo {

   static class Work implements Runnable{
        private int workerNum;//工人的工号
        private Semaphore semaphore;//机器数

        public Work(int workerNum,Semaphore semaphore){
            this.workerNum = workerNum;
            this.semaphore = semaphore;
        }
        public void run() {
            try {
                //1.工人要获取机器
                semaphore.acquire();
                //2.打印工人获取到机器,开始工作
                String name = Thread.currentThread().getName();
                System.out.println(name+"获取到机器开始工作...");
                //3.线程睡眠1000毫秒,模拟工人使用机器过程
                Thread.sleep(1000);
                //4.使用完毕,释放机器,打印工人使用完毕,释放机器
                semaphore.release();
                System.out.println(name+"使用完毕,释放机器!");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }
    }

    public static void main(String[] args) {
        int wokers = 8;//工人数
        Semaphore semaphore = new Semaphore(3);//机器数
        for(int i=0;i<wokers;i++){
            new Thread(
                    new Work(i,semaphore)
            ).start();
        }
    }
}

6.小结

wait和notify区别

  • wait和notify都是object中的方法
  • wait和notify执行前线程都必须获得对象锁
  • wait的作用是使当前线程进行等待
  • notify的作用是通知其他等待当前线程的对象锁的线程

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值