1. 总体介绍
声明一下,我也是在别的小伙伴的代码中修改总结,形成自己的东西....
线程间通信的模型有两种:共享内存和消息传递
- 线程之间通过volatile来通信
- 利用notify和wait来实现线程之间的通信
- 利用LockSupport来实现线程之间的通信
- 利用CountDownLatch来实现线程之间的通信
- 利用ReentrantLock来实现线程之间的通信
- FutureTask.get()线程之间的通信
2. demo举例
2.1. 线程之间通过volatile来通信
/**
* @description: 线程之间通过volatile来通信
* 线程间通信的模型有两种:共享内存和消息传递
* @author: Ding Yawu
* @create: 2022/6/30 2:09 下午
*/
public class TestSync {
/**
* 定义共享变量来实现通信,它需要volatile修饰,否则线程不能及时感知
*/
static volatile boolean noticeFlag = false;
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println(Thread.currentThread().getName() + "添加元素,此时list的size为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5){
noticeFlag = true;
}
}
}, "thread-A");
//线程B
Thread threadB = new Thread(() -> {
while (true) {
if (noticeFlag) {
System.out.println(Thread.currentThread().getName() + "收到通知,开始执行自己的业务...");
break;
}
}
}, "thread-B");
//需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 再启动线程A
threadA.start();
}
}
2.2 利用notify和wait来实现线程之间的通信
/**
* @description: 利用notify和wait来实现线程之间的通信
* wait/notify 必须配合 synchronized 使用,wait 方法释放锁,notify 方法不释放锁。
* wait 是指在一个已经进入了同步锁的线程内,让自己暂时让出同步锁,以便其他正在等待此锁的线程可以得到同步锁并运行,
* 只有其他线程调用了notify()notify并不释放锁,只是告诉调用过wait()的线程可以去参与获得锁的竞争了,但不是马上得到锁,
* 因为锁还在别人手里,别人还没释放,调用 wait() 的一个或多个线程就会解除 wait 状态,重新参与竞争对象锁,程序如果可以再次得到锁,就可以继续向下运行。
* @author: Ding Yawu
* @create: 2022/6/30 2:14 下午
*/
public class TestSync1{
public static void main(String[] args) {
//定义一个锁对象
Object lock = new Object();
List<String> list = new ArrayList<>();
// 线程A
Thread threadA = new Thread(() -> {
synchronized (lock) {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println(Thread.currentThread().getName() + "添加元素,此时list的size为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5) {
//唤醒B线程, notify() 不释放锁,而 wait() 释放锁
lock.notify();
}
}
}
},"thread-A");
//线程B
Thread threadB = new Thread(() -> {
while (true) {
synchronized (lock) {
if (list.size() != 5) {
try {
System.out.println(Thread.currentThread().getName() + "刚开始执行...");
lock.wait();
System.out.println(Thread.currentThread().getName() + "被唤醒了...");
System.out.println(Thread.currentThread().getName() + "收到通知,开始执行自己的业务...");
break;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}, "thread-B");
//需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
2.3. 利用LockSupport来实现线程之间的通信
/**
* @description: 利用LockSupport来实现线程之间的通信
* @author: Ding Yawu
* @create: 2022/6/30 2:26 下午
*/
public class TestSync2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//线程B
Thread threadB = new Thread(() -> {
if (list.size() != 5) {
LockSupport.park();
}
System.out.println("线程B收到通知,开始执行自己的业务...");
}, "thread-B");
//线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println("线程A添加元素,此时list的size为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5) {
LockSupport.unpark(threadB);
}
}
}, "thread-A");
threadA.start();
threadB.start();
}
}
2.4 利用CountDownLatch来实现线程之间的通信
2.4.1 多个线程之间的通信demo1
/**
* @description: 利用CountDownLatch来实现线程之间的通信
* CountDownLatch 基于 AQS 框架,相当于也是维护了一个线程间共享变量 state。
* @author: Ding Yawu
* @create: 2022/6/30 2:26 下午
*/
public class TestSync3 {
public static void main(String[] args) {
CountDownLatch countDownLatch = new CountDownLatch(1);
List<String> list = new ArrayList<>();
//线程A
Thread threadA = new Thread(() -> {
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println(Thread.currentThread().getName() + "添加元素,此时list的size为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5){
countDownLatch.countDown();
}
}
},"thread-A");
//线程B
Thread threadB = new Thread(() -> {
while (true) {
if (list.size() != 5) {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "收到通知,开始执行自己的业务...");
break;
}
}, "thread-B");
//需要先启动线程B
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//再启动线程A
threadA.start();
}
}
2.4.2 原生zk客户端API连接阻塞回调方法的demo2
/**
* zookeeper原生api
* 只能监听一次
一般我们好多中间件都是异步连接,然后客户端重写回调方法得到连接的结果,一般连接失败我们就会
有相应的措施,异步就拆分成两个线程进行操作了,从而提高效率
但有的时候我们就需要同步,这个时候就用到countDownLatch来进行消息传递
*/
public class TestConnect {
ZooKeeper zkClient = null;
public static void main(String[] args) throws Exception {
try {
long start = System.currentTimeMillis();
String zkUrl = "1.117.109.40:2181,1.117.109.40:2182,1.117.109.40:2183";
int sessionTimeout = 20000;
final CountDownLatch countDownLatch = new CountDownLatch(1);
ZooKeeper zooKeeper = new ZooKeeper(zkUrl, sessionTimeout, new Watcher() {
@Override
public void process(WatchedEvent event) {
//收到事件通知后的回调函数(应该是我们自己的事件处理逻辑)
System.out.println(event.getType() + "----" + event.getPath());
if (Event.KeeperState.SyncConnected == event.getState()){
countDownLatch.countDown();
System.out.println("建立连接成功");
}
}
});
countDownLatch.await();
System.out.println("连接状态:" + zooKeeper.getState());
long total = System.currentTimeMillis() - start;
System.out.println("连接总共花费时间:" + total);
zooKeeper.close();
}catch (Exception e){
System.out.println(e);
}
}
}
2.5. 利用ReentrantLock来实现线程之间的通信
/**
* @description: 利用ReentrantLock来实现线程之间的通信
* @author: Ding Yawu
* @create: 2022/6/30 2:26 下午
*/
public class TestSync4 {
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
Condition condition = lock.newCondition();
List<String> list = new ArrayList<>();
//线程A
Thread threadA = new Thread(() -> {
lock.lock();
for (int i = 1; i <= 10; i++) {
list.add("abc");
System.out.println(Thread.currentThread().getName() + "添加元素,此时list的size为:" + list.size());
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
if (list.size() == 5){
condition.signal();
}
}
lock.unlock();
}, "thread-A");
//线程B
Thread threadB = new Thread(() -> {
lock.lock();
if (list.size() != 5) {
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println(Thread.currentThread().getName() + "收到通知,开始执行自己的业务...");
lock.unlock();
}, "thread-B");
threadB.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadA.start();
}
}
2.6. FutureTask.get()线程之间的通信
/**
* FutureTask.get()线程之间的通信
* @author dingyawu
* @version 1.0
* @date created in 2021-08-21 20:35
*/
public class MakeTeaExample1 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 创建线程2的FutureTask
FutureTask<String> ft2 = new FutureTask<String>(new T2Task());
// 创建线程1的FutureTask
FutureTask<String> ft1 = new FutureTask<String>(new T1Task(ft2));
executorService.submit(ft1);
executorService.submit(ft2);
String result = ft1.get();
System.out.println(result);
executorService.shutdown();
}
static class T1Task implements Callable<String> {
private FutureTask<String> ft2;
public T1Task(FutureTask<String> ft2) {
this.ft2 = ft2;
}
@Override
public String call() throws Exception {
System.out.println("T1:洗水壶...");
TimeUnit.SECONDS.sleep(1);
System.out.println("T1:烧开水...");
TimeUnit.SECONDS.sleep(15);
String t2Result = ft2.get();
System.out.println("T1 拿到T2的 " + t2Result + ", 开始泡茶");
return "T1: 上茶!!!";
}
}
static class T2Task implements Callable<String> {
@Override
public String call() throws Exception {
System.out.println("T2:洗茶壶...");
TimeUnit.SECONDS.sleep(1);
System.out.println("T2:洗茶杯...");
TimeUnit.SECONDS.sleep(2);
System.out.println("T2:拿茶叶...");
TimeUnit.SECONDS.sleep(1);
return "福鼎白茶";
}
}
}