线程通信的6种方案,后续不停的补充.......

170 阅读4分钟

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 "福鼎白茶";
        }
    }
}