线程间通信总结
1、volatile关键字
volatile可见性,线程间共享同一变量,volatile修饰,保证线程间可见性。
import java.util.Date;
public class OTest {
private static volatile boolean flag = false;
public static void main(String[] args) {
Thread threadB = new Thread(() -> {
while (!flag) {
try {
Thread.sleep(1000);
System.out.println(new Date());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
threadB.start();
Thread threadA = new Thread(() -> {
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
});
threadA.start();
}
}
2、使用 Object 类的 wait()/notify()
Object 类提供了线程间通信的方法:wait()、notify()、notifyAll(),它们是多线程通信的基础,而这种实现方式的思想自然是线程间通信。
注意:wait/notify 必须配合 synchronized 使用,wait 方法释放锁,notify 方法不释放锁。wait 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,只有其他线程调用了notify(),notify并不释放锁,只是告诉调用过wait()的线程可以去参与获得锁的竞争了,但不是马上得到锁,因为锁还在别人手里,别人还没释放,调用 wait() 的一个或多个线程就会解除 wait 状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。
public class OTest2 {
public static void main(String[] args) {
//定义一个锁对象
Object lock = new Object();
// 线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("线程A释放锁,唤醒B");
lock.notify();
}
}
});
//线程B
Thread threadB = new Thread(() -> {
synchronized (lock) {
try {
System.out.println("线程B等待前处理");
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B收到通知,开始执行自己的业务...");
}
});
//需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
由输出结果,在线程 A 发出 notify() 唤醒通知之后,依然是走完了自己线程的业务之后,线程 B 才开始执行,正好说明 notify() 不释放锁,而 wait() 释放锁。
3、使用JUC工具类 CountDownLatch
jdk1.5 之后在java.util.concurrent包下提供了很多并发编程相关的工具类,简化了并发编程代码的书写,CountDownLatch 基于 AQS 框架,相当于也是维护了一个线程间共享变量 state。
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CountDownLatch;
public class OTest2 {
public static void main(String[] args) {
List<String> param = Arrays.asList("a", "b", "c");
CountDownLatch countDownLatch = new CountDownLatch(param.size());
long start = System.currentTimeMillis();
for (int i = 0; i < param.size(); i++) {
Thread thread = new Thread(() -> {
try {
System.out.println(Thread.currentThread().getName());
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
countDownLatch.countDown();
}
});
thread.start();
}
try {
countDownLatch.await();
System.out.println("所有线程执行完毕,耗时:" + (System.currentTimeMillis() - start));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
4、Condition 线程间通信
Condition的强大之处在于它可以为多个线程间建立不同的Condition, 使用synchronized/wait()只有一个阻塞队列,notifyAll会唤起所有阻塞队列下的线程,而使用lock/condition,可以实现多个阻塞队列,signalAll只会唤起某个阻塞队列下的阻塞线程。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class OTest4 {
public static void main(String[] args) {
//定义一个锁对象
ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();
// 线程A
Thread threadA = new Thread(() -> {
reentrantLock.lock();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("线程A释放锁,唤醒B");
condition.signalAll();
reentrantLock.unlock(); // 需要主动释放锁
}
});
//线程B
Thread threadB = new Thread(() -> {
reentrantLock.lock();
try {
System.out.println("线程B等待前处理");
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程B收到通知,开始执行自己的业务...");
});
//需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
5、基本 LockSupport 实现线程间的阻塞和唤醒
LockSupport 是一种非常灵活的实现线程间阻塞和唤醒的工具,使用它不用关注是等待线程先进行还是唤醒线程先运行,但是得知道线程的名字。
import java.util.concurrent.locks.LockSupport;
public class OTest5 {
public static void main(String[] args) {
//线程B
Thread threadB = new Thread(() -> {
System.out.println("线程B等待前处理");
LockSupport.park();
System.out.println("线程B收到通知,开始执行自己的业务...");
});
Thread threadA = new Thread(() -> {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("线程A释放锁,唤醒B");
if (threadB != null) {
LockSupport.unpark(threadB);
}
});
//需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!