你好,我是吴计可师,一个工作十多年的后端开发,曾就职京东、阿里等多家互联网头部企业。
文章可能会比较长,主要解析的非常详解,或涉及一些底层知识,供面试高阶难度用。可以根据自己实际理解情况合理取舍阅读
线程间通信是指多个线程之间如何交换数据、通知彼此的执行状态以及协调工作。线程间通信在并发编程中是一个重要的概念,它可以让多个线程共同工作,协调资源的访问,避免竞态条件和死锁。
最简单的线程通信方式是通过共享内存(共享变量)来传递数据。多个线程可以访问同一个变量,通过读取和修改共享的变量来交换信息。常见的做法是通过同步来避免并发访问带来的数据不一致性。
Object
类提供了 wait()
和 notify()
方法,这两个方法是线程间通信的基础。wait()
用于让当前线程等待,直到其他线程调用 notify()
或 notifyAll()
来唤醒它。notify()
用于唤醒一个正在等待该对象锁的线程,notifyAll()
用于唤醒所有等待该对象锁的线程。一个常见的例子是生产者和消费者问题,生产者将数据放入缓冲区,消费者从缓冲区取出数据。如果缓冲区为空,消费者需要等待;如果缓冲区满,生产者需要等待。
class SharedBuffer {
private int data = -1;
private boolean available = false;
public synchronized void produce(int value) throws InterruptedException {
while (available) {
wait(); // 如果有数据,生产者等待
}
data = value;
available = true;
System.out.println("Produced: " + value);
notify(); // 唤醒消费者
}
public synchronized int consume() throws InterruptedException {
while (!available) {
wait(); // 如果没有数据,消费者等待
}
available = false;
System.out.println("Consumed: " + data);
notify(); // 唤醒生产者
return data;
}
}
class Producer extends Thread {
private SharedBuffer buffer;
public Producer(SharedBuffer buffer) {
this.buffer = buffer;
}
public void run() {
try {
for (int i = 1; i <= 5; i++) {
buffer.produce(i);
Thread.sleep(500);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer extends Thread {
private SharedBuffer buffer;
public Consumer(SharedBuffer buffer) {
this.buffer = buffer;
}
public void run() {
try {
for (int i = 1; i <= 5; i++) {
buffer.consume();
Thread.sleep(1000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadCommunicationExample {
public static void main(String[] args) {
SharedBuffer buffer = new SharedBuffer();
Producer producer = new Producer(buffer);
Consumer consumer = new Consumer(buffer);
producer.start();
consumer.start();
}
}
在这个例子中,生产者和消费者通过 wait()
和 notify()
方法进行同步。生产者在生产时,如果数据已经存在,它会调用 wait()
使自己进入等待状态,直到消费者消费掉数据并唤醒它;消费者在消费时,如果没有数据,它会调用 wait()
,直到生产者生产数据并唤醒它。
BlockingQueue
是 Java 提供的一种线程安全的队列,常用于生产者-消费者模型中。它实现了阻塞和非阻塞方法,线程在从空队列中取数据时会被阻塞,直到队列中有数据;线程在向满队列中放数据时,也会被阻塞,直到队列有空间。常见的实现类有 ArrayBlockingQueue
和 LinkedBlockingQueue
。
ExecutorService
是 Java 并发包中用于管理线程池的类,它可以通过 Future
对象来实现线程间的通信,Future
允许线程间共享计算结果。ExecutorService
和 Future
进行线程间通信import java.util.concurrent.*;
class Task implements Callable<Integer> {
public Integer call() throws Exception {
System.out.println("Task started");
Thread.sleep(2000); // 模拟耗时操作
return 42;
}
}
public class ThreadCommunicationExecutorExample {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<Integer> future = executor.submit(new Task());
// 在此可以做其他工作
System.out.println("Doing other work...");
// 获取结果,阻塞直到任务完成
Integer result = future.get();
System.out.println("Task result: " + result);
executor.shutdown();
}
}
在这个例子中,Future 提供了 get() 方法来获取异步任务的结果,get() 方法会阻塞,直到任务完成并返回结果。
在多线程环境中,线程往往需要协调工作,避免冲突,同时保证共享资源的正确访问。
在这个例子中,Future 提供了 get() 方法来获取异步任务的结果,get() 方法会阻塞,直到任务完成并返回结果。
一个线程需要等待另一个线程完成某项工作,才能继续执行。
生产者-消费者问题等需要线程之间的协调。
wait() / notify() / notifyAll():这些方法是 Object 类的一部分,可以用于线程间的通信和同步。
BlockingQueue:如 ArrayBlockingQueue, LinkedBlockingQueue 等,这些是线程安全的队列,用于生产者-消费者模式。
CountDownLatch:用于等待某些线程完成后再继续执行。
CyclicBarrier:用于让一组线程等待彼此完成某项工作后再继续执行。
Semaphore:用于控制同时访问某些资源的线程数量。
Exchanger:允许两个线程交换对象。
CountDownLatch:它用于使一个或多个线程等待其他线程完成某项操作后再继续执行。适用于一次性的等待场景,比如等待多个线程执行完毕。它是不可重用的。
示例:等待所有线程执行完毕后,主线程再执行。
CyclicBarrier:它也用于让多个线程在某个点上同步,并在所有线程到达该点后一起继续执行。与 CountDownLatch 不同,CyclicBarrier 是可重用的,适合循环使用。
Exchanger 是一个用于线程间数据交换的同步工具类,它允许两个线程交换数据。每个线程提供一个数据对象,并在交换时交换这些对象。
import java.util.concurrent.*;
class ExchangeTask implements Runnable {
private final Exchanger<String> exchanger;
public ExchangeTask(Exchanger<String> exchanger) {
this.exchanger = exchanger;
}
@Override
public void run() {
try {
String data = "Data from " + Thread.currentThread().getName();
String received = exchanger.exchange(data); // 与另一个线程交换数据
System.out.println(Thread.currentThread().getName() + " received: " + received);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ExchangerExample {
public static void main(String[] args) {
Exchanger<String> exchanger = new Exchanger<>();
Thread thread1 = new Thread(new ExchangeTask(exchanger));
Thread thread2 = new Thread(new ExchangeTask(exchanger));
thread1.start();
thread2.start();
}
}
今天的内容就分享到这儿,喜欢的朋友可以关注,点赞。有什么不足的地方欢迎留言指出,您的关注是我前进的动力!