005并发编程基础-线程之间的通信

本文探讨了线程间通信的三种方法:通过volatile确保变量可见性、利用wait/notify进行同步控制,以及采用CountDownLatch实现无锁通信。通过具体代码示例,详细解析了每种方法的工作原理及适用场景。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一.问题一引入。
  • 在一个类中有一个容器list,线程1每次向容器中添加一个元素,一共添加10个,线程2开启一个循环,检测当list的大小等于5时,终止程序,那么首先线程2能否检测出list大小变化呢?程序如下:
public class ThreadCommunication {
    //没有用volitile修饰,不能保证变量在多个线程中的可见性。
    private  static List<Integer> list=new ArrayList<>();   

    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i < 10; i++) {
                    System.out.println("当前线程大小为: " + list.size());
                    list.add(i);
                }
            }
        }, "t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                for (; ; ) {
                    if (list.size() == 5) {
                        System.out.println("t2线程退出...");
                        throw new RuntimeException("收到通知,线程退出");
                    }
                }
            }
        }, "t2");
        
        t1.start();
        t2.start();
    }

}

      运行结果应该是线程2无法终止的,不过,这要取决于你的JDK是client还是server端,如果是server,那么程序优化高,编译彻底,性能会有很大提升,那么性能高必然会牺牲一些东西,这个时候是线程2是检测不出来的,如果是client端,那么启动快,是一个轻量级的编译器,这个时候是线程2能够检测出来的,然后抛出异常退出。

  • 那如果安装的是server端,要怎样线程2才能感知到呢?
          答案就是volitile关键字,对于list变量使用volitile修饰,保证变量在多个线程中的可见性,这里不再演示。
二.问题二引入.

      还是问题一中的代码,线程2在一个for循环中不断循环去检测,这样是比较浪费性能的,有没有一种更好的方式?答案就是wait/notify,代码如下所示:

public class ThreadCommunication {

    private volatile static List<Integer> list=new ArrayList<>();
    private static Object lock=new Object();
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    for (int i = 0; i < 10; i++) {
                        System.out.println("当前线程大小为: " + list.size());
                        list.add(i);
                        if(list.size()==5){
                            lock.notify();     //唤醒线程1
                            try {
                                lock.wait();    //释放锁
                            } catch (InterruptedException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
            }
        }, "t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                synchronized (lock){
                    if(list.size()!=5){
                        try {
                            lock.wait();    
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2线程退出...");
                    lock.notify();      //在抛出异常之前换醒线程2,使线程2继续执行
                    throw new RuntimeException("收到通知,线程退出");
                }
            }
        }, "t2");

        t2.start();
        t1.start();


    }

}

      运行结果如下:可以看到线程1正常退出,线程2也可以全部执行完毕。
在这里插入图片描述

三.问题三引入:

      使用wait/notify势必会用到锁,那有没有另一种方法不使用锁也可以达到上面的效果呢?答案就是并发包中的countDownLatch类.代码如下:

package 线程;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class ThreadCommunication {

    private volatile static List<Integer> list=new ArrayList<>();
    //构造参数为执行await之后代码的条件--conutDown的总数
    //如果为2,代表执行两次countDown之后,才会执行await之后的代码
    private static CountDownLatch countDownLatch=new CountDownLatch(1);
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                    for (int i = 0; i < 10; i++) {
                        System.out.println("当前线程大小为: " + list.size());
                        list.add(i);
                        if(list.size()==5){
                            countDownLatch.countDown(); 
                        }
                    }

            }
        }, "t1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                    if(list.size()!=5){
                        try {
                            countDownLatch.await();  //一直阻塞,直到执行countDown才继续执行下面的代码
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                    System.out.println("t2线程退出...");
                    throw new RuntimeException("收到通知,线程退出");
                }

        }, "t2");

        t2.start();
        t1.start();


    }

}

      执行结果如下所示:
在这里插入图片描述
总结:CountDownLatch比较经典的应用场景就是一个或多个线程需要达到某种条件之后才去继续执行。
           这里再 给出一个具体的应用场景,就是我们想要测试redis的性能,往redis去存数据,用了50个线程,每个线程并发的去存储1w条数据,那么我们怎么知道这50个线程什么时候都执行完毕?看如下代码思路即可.
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员bling

义父,感谢支持

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值