线程通信问题

最近看了一篇关于线程通信的文章,把其中涉及的代码都看了一下。对其中容易出错的细节进行了批注以及补充,作笔记使用。
原文地址https://mp.weixin.qq.com/s/weETO43GKX5yEUPCWYi3wQ
感谢作者的分享
在多线程环境中,线程之间经常需要协同合作,线程之间的协同就会涉及到线程通信的问题。
线程之间通信的方式有很多,我们主要说比较常用的4种.
1 .wait+notify 方式
2 .join 方式
3 .CountDownLatch 方式
4 .CyclicBarrier 方式

下面依次介绍。
1 .wait+notify方式
假设此时有6个任务,A1,A2,A3,B1,B2,B3。
我们需要让A1先执行,然后执行B1,B2,B3。然后再执行A2,A3。

public static void demo2() {
        final Object lock = new Object();
        final Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    System.out.println("A1");
                    try {
                        //调用wait,notify,notifyAll时,必须要拥有该对象的锁
                        //如果没有拥有该对象的锁却调用该对象的wait,notify,notifyAll方法
                        //就会报出IllegalMonitorStateException异常
                        System.out.println("waiting......");
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("A2");
                    System.out.println("A3");
                }
            }
        });
        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    System.out.println("B1");
                    System.out.println("B2");
                    System.out.println("B3");
                    lock.notify();
                }
            }
        });
        A.start();
        B.start();
    }

关于此处为何要使用synchronized同步代码块,注释处做了说明。
下面简单说一下流程
demo1方法体中共新建了2个线程,分别为线程A和线程B。
1 .线程A首先启动,获取lock锁,进入synchronized同步代码块。
2 .执行打印语句 System.out.println(“A1”);
3 .然后执行lock.wait( )进入等待状态,等待执行下面的语句。同时释放lock对象锁。
4 .线程B启动,没有lock锁,等待A线程释放锁,阻塞。
5 .A释放锁,线程B获取到lock锁,进入线程B的synchronized同步代码块
6 .执行打印语句
System.out.println(“B1”);
System.out.println(“B2”);
System.out.println(“B3”);
7 .执行lock.notify( )唤醒正在等待的A线程,A线程执行剩下的打印语句
执行结果如下:

A1
waiting......
B1
B2
B3
A2
A3

2 .join方式
thread.join意为:等待该线程死亡。如果一个线程A执行了thread.join()语句,其含义是:当前线程A等待thread线程终止之后才从thread.join()返回。
例如:我们有6个任务,A1,A2,A3,B1,B2,B3
我们想要依次执行这些任务,即A1, A2, A3, B1,B2,B3。
代码实现如下:

public static void demo1() throws InterruptedException {
        final Thread A = new Thread(new Runnable() {
            @Override
            public void run() {
                printNumber("A");
            }
        });
        Thread B = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("B开始等待A");
                try {
                    A.join();//join方法意为等待该线程终止,A执行完之后开始执行B
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                printNumber("B");
            }
        });
        A.start();
        B.start();
    }

执行结果如下:

B开始等待A
A print: A1
A print: A2
A print: A3
B print: B1
B print: B2
B print: B3

流程如下:
新建两个线程,线程A和B
1 .在线程B中进行A.join()。意为:等待线程A执行结束
2 .线程A结束之后,线程B开始执行

3 .CountDownLatch类
CountDownLatch是ava.util.concurrent包中的类
例如:我们有4个线程,线程A,B,C,D
我们需要A,B,C同步执行。最后再执行D
代码如下:

public static void demo3() {
        final CountDownLatch countDownLatch = new CountDownLatch(3);
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    countDownLatch.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("All Done ,D start working...");
                System.out.println("D");
            }
        }).start();
        for (char i = 'A'; i <= 'C'; i++) {
            final String tn = String.valueOf(i);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(tn + " is working...");
                    countDownLatch.countDown();
                }
            }).start();
        }
    }

执行结果如下:

A is working...
C is working...
B is working...
All Done ,D start working...
D

流程如下:
1 .CountDownLatch 类构造器中初始化3个线程,意为:当3个线程执行完之后,最后执行D
2 .await方法。先调用await方法判断CountDownLatch中是否有初始化的线程,有的话就等待。
3 .countDown方法,每次执行一个线程后,使用该方法将CountDownLatch 中的线程数减一。当CountDownLatch 中的线程数为0时,立即唤醒正在await的方法。

4 .CyclicBarrier类
假设我们有以下需求。我们有4个线程A,B,C。我们想要3个线程都准备好之后,再一起进行执行。
代码如下:

public static void demo4() {
        final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
        for (char i = 'A'; i <= 'C'; i++) {
            final String tn = String.valueOf(i);
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        System.out.println(tn+" 准备好了,等待其他人");
                        cyclicBarrier.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    } catch (BrokenBarrierException e) {
                        e.printStackTrace();
                    }
                    System.out.println(tn+" 开始运行");
                }
            }).start();
        }
    }

执行结果如下:

A 准备好了,等待其他人
C 准备好了,等待其他人
B 准备好了,等待其他人
A 开始运行
C 开始运行
B 开始运行

流程如下:
1 .我们初始化好3个线程,然后在每个线程中都调用cyclicBarrier.await()方法。进入等待状态,等待其他线程准备好
2 .当所有的线程都执行了该方法之后,CyclicBarrier类判断到此时所有的线程都已经准备好。此时唤醒等待的所有线程。同时执行线程中的方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值