为什么多线程之间需要通信?
在一些需要多个线程通通完成一个任务的场景下,需要线程之间的互相协调和同步。比如一个线程的执行依赖于另一个线程的结果、多个线程需要按顺序协调交替运行。
多线程通信大致可以分为两种 —— 共享内存 & 消息传递
1 共享内存
多个线程可以访问同一个共享的内存区域,通过读取和写入内存中的数据来进行通讯同步。比如堆中的对象,在保证可见性的情况下,直接用共享对象进行通信是一种解决方案
private static volatile Integer num = 0;
@Test
public void 共享变量通信() {
new Thread(()->{
while (num <= 10) {
while(num % 2 != 1){}
if (num <= 10) System.out.println(Thread.currentThread().getName() + " " + num);
num++;
}
}, "thread_odd ").start();
new Thread(()->{
while (num <= 10) {
while(num % 2 != 0){}
if (num <= 10) System.out.println(Thread.currentThread().getName() + " " + num);
num++;
}
}, "thread_even").start();
}
2 消息传递
多个线程可以通过以下方式进行消息传递:
- 管程机制(也就是
synchronized
的wait() / notify()
机制以及ReentrantLock
的条件变量Condition
的await() / signal()
机制) - 线程计数调度的工具类:
CountDownLatch
、CyclicBarrier
- 信号量机制:
Semaphore
Future
体系:通过future.get()
阻塞等待其他线程结束返回结果CompletableFuture
:用链式编程的方式优雅地编排线程的运行流程CompletionService
:结合一个阻塞队列实现一个生成者/消费者模型- 通过 Future 多线程访问不同数据源得到数据
- 每次通过一个线程的 future.get() 会造成队头阻塞问题,如果第一个线程时间很长,其他线程就算成功了也要等待
- CompletionService 通过加入一个阻塞队列实现一个灵活的生成者/消费者模型。
- 阻塞队列方式