目录
1、Object.wait()与Object.notify()/notifyAll()
3、Condition.await() Condition.signal()/signalAll()
4、CountDownLatch/CyclicBarrier
本文关注Java多线程之间,如何进行通信?当一个复杂任务由多个线程共同完成时,线程与线程之间,如何进行协作,本文主要以代码的形式演示Java中常用的工具,以实现多线程之间通信。
而多线程实现对互斥资源的访问,请参考我的另一篇文章:Java锁汇总
1、Object.wait()与Object.notify()/notifyAll()
参考文档:如何在 Java 中正确使用 wait, notify 和 notifyAll – 以生产者消费者模型为例
面试题:利用Obejct.wait()与Object.notify()/notifyAll()来实现生产者消费者模型
package com.autocoding.juc;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
/**
* Simple Java program to demonstrate How to use wait, notify and notifyAll()
* method in Java by solving producer consumer problem.
*
* @author Javin Paul
*/
public class WaitNotifyTest {
public static void main(String args[]) {
System.out.println("How to use wait and notify method in Java");
System.out.println("Solving Producer Consumper Problem");
final Queue<Integer> buffer = new LinkedList<>();
final int maxSize = 10;
final Thread producer = new Producer(buffer, maxSize, "PRODUCER");
final Thread consumer = new Consumer(buffer, maxSize, "CONSUMER");
producer.start();
consumer.start();
}
}
/**
* Producer Thread will keep producing values for Consumer to consumer. It will
* use wait() method when Queue is full and use notify() method to send
* notification to Consumer Thread.
*
* @author WINDOWS 8
*
*/
class Producer extends Thread {
private final Queue<Integer> queue;
private final int maxSize;
public Producer(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.size() == maxSize) {
try {
System.out.println("Queue is full, " + "Producer thread waiting for "
+ "consumer to take something from queue");
queue.wait();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
final Random random = new Random();
final int i = random.nextInt();
System.out.println("Producing value : " + i);
queue.add(i);
queue.notifyAll();
}
}
}
}
/**
* Consumer Thread will consumer values form shared queue. It will also use
* wait() method to wait if queue is empty. It will also use notify method to
* send notification to producer thread after consuming values from queue.
*
* @author WINDOWS 8
*
*/
class Consumer extends Thread {
final private Queue<Integer> queue;
final private int maxSize;
public Consumer(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
synchronized (queue) {
while (queue.isEmpty()) {
System.out.println("Queue is empty," + "Consumer thread is waiting"
+ " for producer thread to put something in queue");
try {
queue.wait();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
System.out.println("Consuming value : " + queue.remove());
queue.notifyAll();
}
}
}
}
2、Thread.join()
面试题:新建 T1、T2、T3 三个线程,如何保证它们按顺序执行?
代码如下:
package com.autocoding.juc;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
*
* 面试题:新建 T1、T2、T3 三个线程,如何保证它们按顺序执行? 期望执行顺序:T1、T2、T3
*/
@Slf4j
public class Thread$JoinTest {
private static T1 t1 = new T1("T1");
private static T2 t2 = new T2("T2");
private static T3 t3 = new T3("T3");
public static void main(String[] args) {
Thread$JoinTest.t3.start();
Thread$JoinTest.t2.start();
Thread$JoinTest.t1.start();
}
private static class T1 extends Thread {
public T1(String name) {
super(name);
}
@Override
public void run() {
Thread$JoinTest.log.info("当前线程T1:开始运行");
try {
TimeUnit.SECONDS.sleep(5);
} catch (final InterruptedException e) {
e.printStackTrace();
}
Thread$JoinTest.log.info("当前线程T1:结束运行");
}
}
private static class T2 extends Thread {
public T2(String name) {
super(name);
}
@Override
public void run() {
try {
Thread$JoinTest.t1.join();
} catch (final InterruptedException e) {
e.printStackTrace();
}
Thread$JoinTest.log.info("当前线程T2:开始运行");
try {
TimeUnit.SECONDS.sleep(5);
} catch (final InterruptedException e) {
e.printStackTrace();
}
Thread$JoinTest.log.info("当前线程T2:结束运行");
}
}
private static class T3 extends Thread {
public T3(String name) {
super(name);
}
@Override
public void run() {
try {
Thread$JoinTest.t2.join();
} catch (final InterruptedException e) {
e.printStackTrace();
}
Thread$JoinTest.log.info("当前线程T3:开始运行");
try {
TimeUnit.SECONDS.sleep(5);
} catch (final InterruptedException e) {
e.printStackTrace();
}
Thread$JoinTest.log.info("当前线程T3:结束运行");
}
}
}
运行结果:
3、Condition.await() Condition.signal()/signalAll()
面试题:利用 Condition.await() Condition.signal()/signalAll() 来实现生产者消费者模型
package com.autocoding.juc.threadmessaging;
import java.util.LinkedList;
import java.util.Queue;
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ConditionTest {
private static final ReentrantLock BUFFER_LOCK = new ReentrantLock();
private static final Condition BUFFER_CONDITION = ConditionTest.BUFFER_LOCK.newCondition();
public static void main(String args[]) {
final Queue<Integer> buffer = new LinkedList<>();
final int maxSize = 10;
final Thread producer = new Producer(buffer, maxSize, "PRODUCER");
final Thread consumer = new Consumer(buffer, maxSize, "CONSUMER");
producer.start();
consumer.start();
}
private static class Producer extends Thread {
private final Queue<Integer> queue;
private final int maxSize;
public Producer(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
ConditionTest.BUFFER_LOCK.lock();
try {
while (queue.size() == maxSize) {
try {
System.out.println("Queue is full, " + "Producer thread waiting for "
+ "consumer to take something from queue");
ConditionTest.BUFFER_CONDITION.await();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
final Random random = new Random();
final int i = random.nextInt();
System.out.println("Producing value : " + i);
queue.add(i);
ConditionTest.BUFFER_CONDITION.signalAll();
} finally {
ConditionTest.BUFFER_LOCK.unlock();
}
}
}
}
private static class Consumer extends Thread {
final private Queue<Integer> queue;
final private int maxSize;
public Consumer(Queue<Integer> queue, int maxSize, String name) {
super(name);
this.queue = queue;
this.maxSize = maxSize;
}
@Override
public void run() {
while (true) {
ConditionTest.BUFFER_LOCK.lock();
try {
while (queue.isEmpty()) {
try {
System.out.println("Queue is empty," + "Consumer thread is waiting"
+ " for producer thread to put something in queue");
ConditionTest.BUFFER_CONDITION.await();
} catch (final Exception ex) {
ex.printStackTrace();
}
}
System.out.println("Consuming value : " + queue.remove());
ConditionTest.BUFFER_CONDITION.signalAll();
} finally {
ConditionTest.BUFFER_LOCK.unlock();
}
}
}
}
}
程序运行结果:
4、CountDownLatch/CyclicBarrier
4.1 CountDownLatch
场景:百米赛跑,4名运动员选手到达场地等待裁判口令,裁判一声口令,选手听到后同时起跑,当所有选手到达终点,裁判进行汇总排名。
package com.autocoding.juc.threadmessaging;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 场景:百米赛跑,4名运动员选手到达场地等待裁判口令,裁判一声口令,选手听到后同时起跑,当所有选手到达终点,裁判进行汇总排名。
*
* @author Administrator
*
*/
public class CountdownLatchTest {
public static void main(String[] args) {
final ExecutorService service = Executors.newCachedThreadPool();
final CountDownLatch beginningCDL = new CountDownLatch(1);
final CountDownLatch endingCDL = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
final Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("选手" + Thread.currentThread().getName() + "正在等待裁判发布口令");
beginningCDL.await();
System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
endingCDL.countDown();
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long) (Math.random() * 10000));
System.out.println("裁判" + Thread.currentThread().getName() + "即将发布口令");
beginningCDL.countDown();
System.out.println("裁判" + Thread.currentThread().getName() + "已发送口令,正在等待所有选手到达终点");
endingCDL.await();
System.out.println("所有选手都到达终点");
System.out.println("裁判" + Thread.currentThread().getName() + "汇总成绩排名");
} catch (final InterruptedException e) {
e.printStackTrace();
}
service.shutdown();
}
}
程序运行结果:
4.2 CyclicBarrier
package com.autocoding.juc.threadmessaging;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* 场景:百米赛跑,4名运动员选手到达场地等待裁判口令,裁判一声口令,选手听到后同时起跑,当所有选手到达终点,裁判进行汇总排名。
*
* @author Administrator
*
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
final ExecutorService service = Executors.newCachedThreadPool();
final CyclicBarrier beginningCyclicBarrier = new CyclicBarrier(4, new Runnable() {
@Override
public void run() {
System.out.println("所有选手,都准备好了,比赛可以开始了");
}
});
final CountDownLatch endingCDL = new CountDownLatch(4);
for (int i = 0; i < 4; i++) {
final Runnable runnable = new Runnable() {
@Override
public void run() {
try {
System.out.println("选手" + Thread.currentThread().getName() + "准备好了....");
try {
beginningCyclicBarrier.await();
} catch (final BrokenBarrierException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("选手" + Thread.currentThread().getName() + "已接受裁判口令");
Thread.sleep((long) (Math.random() * 10000));
System.out.println("选手" + Thread.currentThread().getName() + "到达终点");
endingCDL.countDown();
} catch (final InterruptedException e) {
e.printStackTrace();
}
}
};
service.execute(runnable);
}
try {
Thread.sleep((long) (Math.random() * 10000));
endingCDL.await();
System.out.println("所有选手都到达终点");
System.out.println("裁判" + Thread.currentThread().getName() + "汇总成绩排名");
} catch (final InterruptedException e) {
e.printStackTrace();
}
service.shutdown();
}
}
程序运行结果:
5、BlockingQueue
参考文档:使用blockingqueue实现的简单生产者消费者模型
6、Semaphore
利用Semaphore将Set装饰为一个BlockingSet,代码示例,初始化一个容量为5的Set,当向BlockingSet中继续添加元素时,阻塞,当元素删除元素时,又可以继续添加元素了,直到当前元素容量为5 。
请记住Guava中的RateLimiter也是基于Semaphore来实现的。
package com.autocoding.juc.threadmessaging;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.Semaphore;
/**
* 利用JDK中信号量来将SET修饰为一个BlockingSet
* @ClassName: SemaphoreTest
* @author: QiaoLi
* @date: Jan 6, 2021 11:10:14 AM
*/
public class SemaphoreTest {
public static void main(String[] args) throws InterruptedException {
BlockingSet<Integer> blockingSet = new BlockingSet<>(new HashSet<Integer>(), 5);
for (int i = 1; i <= 10; i++) {
blockingSet.add(i);
}
System.err.println("blockingSet:" + blockingSet);
}
private static class BlockingSet<T> implements Set<T> {
private Set<T> set = new HashSet<T>();
private final Semaphore semaphore;
public BlockingSet(Set<? extends T> set, Integer capacity) {
if (null == set) {
throw new IllegalArgumentException("set is null");
}
if (null == capacity) {
throw new IllegalArgumentException("capacity is null");
}
this.set.addAll(set);
this.semaphore = new Semaphore(capacity);
}
@Override
public boolean add(Object e) {
System.out.println("开始存储元素:" + e);
try {
semaphore.acquire();
} catch (InterruptedException e1) {
System.err.println(e1.getMessage());
}
boolean addFlag = false;
try {
addFlag = set.add((T) e);
return addFlag;
} finally {
if (!addFlag) {
semaphore.release();
}
System.err.println("结束存储元素:" + e);
}
}
@Override
public boolean remove(Object o) {
System.out.println("开始删除元素:" + o);
boolean removedFlag = set.remove(o);
if (removedFlag) {
semaphore.release();
}
System.out.println("结束删除元素:" + o);
return removedFlag;
}
@Override
public boolean addAll(Collection c) {
boolean modified = false;
for (Object e : c) {
if (add(e)) {
modified = true;
}
}
return modified;
}
........... 删除无关代码
}
}
程序运行结果: