Java多线程之间如何进行通信?

 

目录

1、Object.wait()与Object.notify()/notifyAll()

2、Thread.join()

3、Condition.await()  Condition.signal()/signalAll()

4、CountDownLatch/CyclicBarrier

4.1 CountDownLatch

4.2 CyclicBarrier

5、BlockingQueue

6、Semaphore


          本文关注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;
		}

		 ........... 删除无关代码

	}

}

程序运行结果:

猜你喜欢

转载自blog.csdn.net/s2008100262/article/details/112093936