Java 如何做线程通信

早上你一边煮着鸡蛋,一边听着闹钟提醒自己7点要出门,等鸡蛋煮好了你会关火盛出——这就是最朴素的“线程通信”:煮鸡蛋的“线程”完成任务后,通知你这个“主线程”来处理下一步。放到编程里,Java的线程通信,本质就是让多个线程像这样有序配合,不抢资源、不卡壳。

为什么需要线程通信?

要是没有通信,多线程会乱成一锅粥。比如你开了一个“打印订单线程”和一个“付款线程”,如果付款线程不管打印线程有没有完成,就直接删除订单,就会出现“先付款后打印”的乌龙;再比如你开了一个“取外卖线程”,一直问“外卖到了吗”,不停占着手机后台,反而耽误了别的任务。线程通信就是要解决这两个问题:让线程按规则干活,别瞎抢,也别瞎耗资源。

Java 如何做线程通信

最常用的两种通信方式

等待-通知机制:最接近生活的配合

这是Java线程通信的核心,用的是wait()和notify()方法,就像你和室友的约定:“我先打扫卫生(线程1),等我扫完(调用wait()),你再用卫生间(线程2),你用完叫我(调用notify())”。

举个生活化的例子:你和朋友合租,约定“客厅垃圾满了就喊人倒”。实现起来就是:

  • 倒垃圾的线程(线程A)先检查“垃圾满了吗”,没满就调用wait(),等着;
  • 你扔垃圾的线程(线程B)扔完垃圾,发现满了,就调用notify(),叫醒倒垃圾的线程;
  • 线程A被唤醒后,再检查“真的满了吗”,确认后去倒。

注意这里要加“双重检查”,因为可能有别的线程误喊,就像你喊朋友倒垃圾,朋友醒了发现其实垃圾没满,就再回去等。

阻塞队列:不用自己管配合的“快递柜”

要是觉得wait()和notify()太麻烦,用阻塞队列更省心。它就像小区的快递柜:你(生产者线程)把快递放进去,放满了就等;别人(消费者线程)取快递,取走后就喊人放。

Java里的ArrayBlockingQueue就是这种工具:比如做外卖系统,“接单线程”是生产者,把订单放进队列;“出餐线程”是消费者,从队列里取订单做。你不用自己写“等放满”“通知取”的代码,队列会自动处理:队列空的时候,消费者自动等;队列满的时候,生产者自动等,还能避免线程混乱。

避坑小提醒

很多新手用线程通信会踩两个坑:一是wait()和notify()必须放在synchronized锁里,就像你和室友的约定,得先说好“用卫生间要先敲门占着”,不然随便喊会出错;二是别一直用循环检查状态,比如一直写while(垃圾没满) { 什么也不做 },这叫“忙等”,会一直占着CPU,就像你一直盯着手机看外卖到了没,啥也干不了,用wait()才会让线程休息,省资源。

其实线程通信本质就是“说好规则,互相配合”,不管是你煮鸡蛋时的等待,还是小区的快递柜,都是这个理。掌握这两种方式,就能解决日常开发里大部分多线程配合的问题了。

上一篇 C / C++ 编程语言的核心特性与入门常见误区科普