线程通信方式有几种(实现线程间通信的几种方法)

前言 开发中不免会遇到需要所有子线程执行完毕通知主线程处理某些逻辑的场景。 或者是线程A在执行到某个条件通知线程B执行某个操作。 可以通过以下几种方式实现: 等待通知机制 等待通知模式是Java中比较经典的线程通信方式。 两个线程通过对同一对象调用等待wait()和通知notify()方法来进行通讯。 如两个线程交替打印奇偶数: publicclassTwoThreadWaitNotify{ pr…

线程间通信的方式有几种?

引言

开发设计中难免会碰到必须全部子线程执行结束通知主线程解决一些逻辑性的情景。

或是是线程 A 在执行到某一标准通知线程 B 执行某一实际操作。

可以利用下面几类方式完成:

等待通知体制

等待通知方式是 Java 中较为传统的线程通讯方式。

2个线程根据对同一目标启用等待 wait() 和通知 notify() 方式来开展通信。

如2个线程更替打印出奇数偶数:

public class TwoThreadWaitNotify {

    private int start = 1;

    private boolean flag = false;

    public static void main(String[] args) {
        TwoThreadWaitNotify twoThread = new TwoThreadWaitNotify();

        Thread t1 = new Thread(new OuNum(twoThread));
        t1.setName(\"A\");


        Thread t2 = new Thread(new JiNum(twoThread));
        t2.setName(\"B\");

        t1.start();
        t2.start();
    }

    /**
     * 双数线程
     */
    public static class OuNum implements Runnable {
        private TwoThreadWaitNotify number;

        public OuNum(TwoThreadWaitNotify number) {
            this.number = number;
        }

        @Override
        public void run() {

            while (number.start <= 100) {
                synchronized (TwoThreadWaitNotify.class) {
                    System.out.println(\"偶数线程抢到锁了\");
                    if (number.flag) {
                        System.out.println(Thread.currentThread().getName()   \" - 双数\"   number.start);
                        number.start  ;

                        number.flag = false;
                        TwoThreadWaitNotify.class.notify();

                    }else {
                        try {
                            TwoThreadWaitNotify.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }

            }
        }
    }


    /**
     * 单数线程
     */
    public static class JiNum implements Runnable {
        private TwoThreadWaitNotify number;

        public JiNum(TwoThreadWaitNotify number) {
            this.number = number;
        }

        @Override
        public void run() {
            while (number.start <= 100) {
                synchronized (TwoThreadWaitNotify.class) {
                    System.out.println(\"单数线程抢到锁了\");
                    if (!number.flag) {
                        System.out.println(Thread.currentThread().getName()   \" - 奇数\"   number.start);
                        number.start  ;

                        number.flag = true;

                        TwoThreadWaitNotify.class.notify();
                    }else {
                        try {
                            TwoThreadWaitNotify.class.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    }
}

輸出結果:

t2 - 单数93
t1 - 双数94
t2 - 单数95
t1 - 双数96
t2 - 单数97
t1 - 双数98
t2 - 单数99
t1 - 双数100

这儿的线程 A 和线程 B 都对同一个目标 TwoThreadWaitNotify.class 获得锁,A 线程启用了同歩目标的 wait() 方式释放出来了锁并进到 WAITING 情况。

B 线程启用了 notify() 方式,那样 A 线程接到通知以后就可以从 wait() 方式中回到。

这儿运用了 TwoThreadWaitNotify.class 目标完成了通讯。

有一些必须留意:

  • wait() 、notify()、notifyAll() 启用的前提条件全是得到了另一半的锁(也可称之为目标监控器)。
  • 启用 wait() 方式后线程会释放出来锁,进到 WAITING 情况,该线程也会被运动到等待序列中。
  • 启用 notify() 方式会将等待序列中的线程挪动到同歩序列中,线程情况也会升级为 BLOCKED
  • 从 wait() 方式回到的先决条件是启用 notify() 方式的线程释放出来锁,wait() 方式的线程得到锁。

等待通知拥有一个經典现代性:

线程 A 做为顾客:

  1. 获得目标的锁。
  2. 进到 while(分辨标准),并启用 wait() 方式。
  3. 当标准达到跳出循环执行实际解决逻辑性。

线程 B 做为经营者:

  1. 获得目标锁。
  2. 变更与线程 A 同用的判定标准。
  3. 启用 notify() 方式。

伪代码如下所示:

//Thread A

synchronized(Object){
    while(标准){
        Object.wait();
    }
    //do something
}

//Thread B
synchronized(Object){
    条件=false;//更改标准
    Object.notify();
}

join() 方式

    private static void join() throws InterruptedException {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"running\");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }) ;
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"running2\");
                try {
                    Thread.sleep(4000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }) ;

        t1.start();
        t2.start();

        //等待线程1停止
        t1.join();
  //等待线程2终止
        t2.join();

        LOGGER.info(\"main over\");
    }

输出結果:

2018-03-16 20:21:30.967 [Thread-1] INFO  c.c.actual.ThreadCommunication - running2
2018-03-16 20:21:30.967 [Thread-0] INFO  c.c.actual.ThreadCommunication - running
2018-03-16 20:21:34.972 [main] INFO  c.c.actual.ThreadCommunication - main over

在 t1.join() 的时候会一直堵塞到 t1 实行结束,因此最后主线程会等待 t1 和 t2 线程实行结束。

实际上从源代码可以看得出,join() 也是运用的等待通告体制:

关键逻辑性:

    while (isAlive()) {
        wait(0);
    }

在 join 线程进行后会启用 notifyAll() 方式,是在 JVM 完成中启用,因此这儿看不出。

volatile 共享内存

由于 Java 是选用共享内存的形式开展线程通讯的,因此可以采取下列方法用主线程关掉 A 线程:

public class Volatile implements Runnable{

    private static volatile boolean flag = true ;

    @Override
    public void run() {
        while (flag){
            System.out.println(Thread.currentThread().getName()   \"已经运作。。。\");
        }
        System.out.println(Thread.currentThread().getName()  \"实行结束\");
    }

    public static void main(String[] args) throws InterruptedException {
        Volatile aVolatile = new Volatile();
        new Thread(aVolatile,\"thread A\").start();


        System.out.println(\"main 线程已经运作\") ;

        TimeUnit.MILLISECONDS.sleep(100) ;

        aVolatile.stopThread();

    }

    private void stopThread(){
        flag = false ;
    }
}

输出結果:

thread A已经运作。。。
thread A已经运作。。。
thread A已经运作。。。
thread A已经运作。。。
thread A实行结束

这儿的 flag 储放于主运行内存中,因此主线程和线程 A 都能够见到。

flag 选用 volatile 装饰主要是为了更好地运行内存由此可见性,大量內容可以查询这儿。

CountDownLatch 高并发专用工具

CountDownLatch 可以完成 join 同样的作用,可是更为的灵便。

    private static void countDownLatch() throws Exception{
        int thread = 3 ;
        long start = System.currentTimeMillis();
        final CountDownLatch countDown = new CountDownLatch(thread);
        for (int i= 0 ;i<thread ; i  ){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    LOGGER.info(\"thread run\");
                    try {
                        Thread.sleep(2000);
                        countDown.countDown();

                        LOGGER.info(\"thread end\");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }).start();
        }

        countDown.await();
        long stop = System.currentTimeMillis();
        LOGGER.info(\"main over total time={}\",stop-start);
    }

输出結果:

2018-03-16 20:19:44.126 [Thread-0] INFO  c.c.actual.ThreadCommunication - thread run
2018-03-16 20:19:44.126 [Thread-2] INFO  c.c.actual.ThreadCommunication - thread run
2018-03-16 20:19:44.126 [Thread-1] INFO  c.c.actual.ThreadCommunication - thread run
2018-03-16 20:19:46.136 [Thread-2] INFO  c.c.actual.ThreadCommunication - thread end
2018-03-16 20:19:46.136 [Thread-1] INFO  c.c.actual.ThreadCommunication - thread end
2018-03-16 20:19:46.136 [Thread-0] INFO  c.c.actual.ThreadCommunication - thread end
2018-03-16 20:19:46.136 [main] INFO  c.c.actual.ThreadCommunication - main over total time=2012

CountDownLatch 也是根据 AQS(AbstractQueuedSynchronizer) 完成的,大量完成参照 ReentrantLock 完成基本原理

  • 复位一个 CountDownLatch 时告知高并发的线程,随后在每一个线程处理完毕以后启用 countDown() 方式。
  • 该方法会将 AQS 内嵌的一个 state 情况 -1 。
  • 最后在主线程启用 await() 方式,它会堵塞直到 state == 0 的情况下回到。

CyclicBarrier 高并发专用工具

    private static void cyclicBarrier() throws Exception {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3) ;

        new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"thread run\");
                try {
                    cyclicBarrier.await() ;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                LOGGER.info(\"thread end do something\");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"thread run\");
                try {
                    cyclicBarrier.await() ;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                LOGGER.info(\"thread end do something\");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"thread run\");
                try {
                    Thread.sleep(5000);
                    cyclicBarrier.await() ;
                } catch (Exception e) {
                    e.printStackTrace();
                }

                LOGGER.info(\"thread end do something\");
            }   }).start();

        LOGGER.info(\"main thread\");
    }

CyclicBarrier 中文名字称为天然屏障或是是护栏,还可以用以线程间通信。

它可以等待 N 个线程都做到某一情况后再次运行的实际效果。

  1. 最先复位线程参与者。
  2. 调用 await() 可能在全部参与者线程都调用以前等待。
  3. 直到全部参与者都调用了 await() 后,所有线程从 await() 回到再次后面逻辑性。

运行結果:

2018-03-18 22:40:00.731 [Thread-0] INFO  c.c.actual.ThreadCommunication - thread run
2018-03-18 22:40:00.731 [Thread-1] INFO  c.c.actual.ThreadCommunication - thread run
2018-03-18 22:40:00.731 [Thread-2] INFO  c.c.actual.ThreadCommunication - thread run
2018-03-18 22:40:00.731 [main] INFO  c.c.actual.ThreadCommunication - main thread
2018-03-18 22:40:05.741 [Thread-0] INFO  c.c.actual.ThreadCommunication - thread end do something
2018-03-18 22:40:05.741 [Thread-1] INFO  c.c.actual.ThreadCommunication - thread end do something
2018-03-18 22:40:05.741 [Thread-2] INFO  c.c.actual.ThreadCommunication - thread end do something

可以看到因为在其中一个线程休眠状态了五秒,全部其他全部的线程都得等待这一线程调用 await() 。

该专用工具可以完成 CountDownLatch 一样的作用,可是要更为灵便。乃至可以调用 reset() 方式重设 CyclicBarrier (必须自主捕获 BrokenBarrierException 解决) 随后再次实行。

线程回应终断

public class StopThread implements Runnable {
    @Override
    public void run() {

        while ( !Thread.currentThread().isInterrupted()) {
            // 线程实行实际逻辑性
            System.out.println(Thread.currentThread().getName()   \"运行中。。\");
        }

        System.out.println(Thread.currentThread().getName()   \"撤出。。\");

    }

    public static void main(String[] args) throws InterruptedException {
        Thread thread = new Thread(new StopThread(), \"thread A\");
        thread.start();

        System.out.println(\"main 线程已经运行\") ;

        TimeUnit.MILLISECONDS.sleep(10) ;
        thread.interrupt();
    }


}

輸出結果:

thread A运行中。。
thread A运行中。。
thread A撤出。。

可以选用终断线程的方法来通讯,调用了 thread.interrupt() 方式实际上便是将 thread 中的一个标示特性置为了更好地 true。

并不是说调用了该方式就可以终断线程,如果不对这一标示开展回应实际上是没什么功效(这儿对这一标示开展了分辨)。

可是假如抛出去了 InterruptedException 出现异常,该标示便会被 JVM 重设为 false。

线程池 awaitTermination() 方式

如果是用线程池来管理方法线程,可以应用下列方法来让主线程等待线程池里全部每日任务实行结束:

    private static void executorService() throws Exception{
        BlockingQueue<Runnable> queue = new LinkedBlockingQueue<>(10) ;
        ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(5,5,1, TimeUnit.MILLISECONDS,queue) ;
        poolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"running\");
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        poolExecutor.execute(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"running2\");
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });

        poolExecutor.shutdown();
        while (!poolExecutor.awaitTermination(1,TimeUnit.SECONDS)){
            LOGGER.info(\"线程仍在实行。。。\");
        }
        LOGGER.info(\"main over\");
    }

輸出結果:

2018-03-16 20:18:01.273 [pool-1-thread-2] INFO  c.c.actual.ThreadCommunication - running2
2018-03-16 20:18:01.273 [pool-1-thread-1] INFO  c.c.actual.ThreadCommunication - running
2018-03-16 20:18:02.273 [main] INFO  c.c.actual.ThreadCommunication - 线程仍在实行。。。
2018-03-16 20:18:03.278 [main] INFO  c.c.actual.ThreadCommunication - 线程仍在实行。。。
2018-03-16 20:18:04.278 [main] INFO  c.c.actual.ThreadCommunication - main over

应用这一 awaitTermination() 方式的前提条件必须关掉线程池,如调用了 shutdown() 方式。

调用了 shutdown() 以后线程池会终止接纳新每日任务,而且会光滑的关掉线程池里目前的每日任务。

管路通讯

    public static void piped() throws IOException {
        //面对于标识符 PipedInputStream 面对于字节数
        PipedWriter writer = new PipedWriter();
        PipedReader reader = new PipedReader();

        //I/O流创建联接
        writer.connect(reader);


        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"running\");
                try {
                    for (int i = 0; i < 10; i  ) {

                        writer.write(i \"\");
                        Thread.sleep(10);
                    }
                } catch (Exception e) {

                } finally {
                    try {
                        writer.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }

            }
        });
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                LOGGER.info(\"running2\");
                int msg = 0;
                try {
                    while ((msg = reader.read()) != -1) {
                        LOGGER.info(\"msg={}\", (char) msg);
                    }

                } catch (Exception e) {

                }
            }
        });
        t1.start();
        t2.start();
    }

输出結果:

2018-03-16 19:56:43.014 [Thread-0] INFO  c.c.actual.ThreadCommunication - running
2018-03-16 19:56:43.014 [Thread-1] INFO  c.c.actual.ThreadCommunication - running2
2018-03-16 19:56:43.130 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=0
2018-03-16 19:56:43.132 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=1
2018-03-16 19:56:43.132 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=2
2018-03-16 19:56:43.133 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=3
2018-03-16 19:56:43.133 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=4
2018-03-16 19:56:43.133 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=5
2018-03-16 19:56:43.133 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=6
2018-03-16 19:56:43.134 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=7
2018-03-16 19:56:43.134 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=8
2018-03-16 19:56:43.134 [Thread-1] INFO  c.c.actual.ThreadCommunication - msg=9

Java 虽然是根据运行内存通讯的,但还可以应用管路通讯。

必须留意的是,键入流和输出流必须最先创建联接。那样进程 B 就可以接到进程 A 传出的最新消息了。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

(0)
上一篇 2022年5月7日 上午10:30
下一篇 2022年5月7日 上午10:31

相关推荐

  • 荔枝上市是什么季节,2020荔枝最新消息

    没有荔枝的夏天是不完整的。 这不,气温越来越高,夏天要来了,荔枝还会远吗? 近日,东莞第一批荔枝“三月红”已经上市啦。喜人的是,第一批早熟荔枝的产量颇丰,预计糯米糍、桂味以及妃子笑等品种的产量也相当好。 早熟荔枝“三月红”上市 在东莞大岭山马蹄岗临近松山湖地段的一处荔枝果园,这里的三月红已经熟了,果农严敬金是这片果园的主人,他告诉记者,他是肇庆人,来到大岭山已经三十年了,在大岭山有三个荔枝园,这是…

    2022年8月7日
    1130
  • 固态硬盘那个牌子好,十大固态硬盘品牌

    最近收到各种状况的影响,固态硬盘持续涨价,很多型号涨价已经超过了50%,今天给大家推荐的这几款SSD都是比较实用和主流的型号,不过价格应该比京东最低价高出不少。 随着SSD容量不断地提升,现在我对大家的建议是尽量选购512G以上容量的SSD型号,超过1T的固态作为仓库盘,我建议大家可以买SATA型号,很便宜一些。 目前最好的几个品牌还是主要集中在传统的几个大品牌,三星、西部数据、英特尔等品牌,从性…

    2022年8月12日
    720
  • 电销机器人怎么样,电销机器人行业现状

    人工智能行业正赶上一个飞速发展的时代,传统电销行业的的问题日益突出,因此电销机器人的出现成为很多企业的选择。对于创业者而言这就是一个大市场,早进入早收割红利,那么加盟电销机器人是否有市场呢? 企业公司为什么需要电销机器人? 1、减低人力成本:一个电话销售员从工资、培训、补贴、奖金一个月做少要4000以上,而一个电销机器人相当于3-5电话销售员,这样算来怎么都是电销机器人划算。 2、提高工作效率:电…

    2022年6月9日
    570
  • 免费照片做成视频的软件叫什么(把照片编辑成视频的技巧)

    短视频普及的时代,每个人都是视频的生产者。大家开始用手机拍摄视频,拍视频简单,大家都会,但会剪辑的人就少了,今天小蚁给大家介绍5款实用又简单的手机剪辑APP,人人都能学会,心动不?学会这些工具,你也可以轻松玩转后期! 1、剪映 推荐指数:★★★★★ 优点:设计简洁,操作无任何难度,还可以拍摄抖音同款热门视频 素材全面,而且都是免费的,不花一分钱,就能出大片 剪辑好的视频可以直接同步到抖音,这个非常…

    2022年5月12日
    730
  • 有哪些推广赚钱的软件(利用公众号赚钱的方法)

    说到网络小说,大部分人并不陌生,追小说如同追剧一样,多看一眼就停不下来。正是因为人的这种好奇心理,加上合理的分销机制,才衍生出了“公众号小说”项目。 有一群这样的公众号,你可能会从各种不同的渠道关注他们:贴吧、微博、Q群、抖音、竞价 公众号小说赚钱 关注之后,他们在这样一个公众号里面,分成“男频”和“女频”两类不同的小说,也有的会专注只做一类。 不管你是男女,总有一类小说你感兴趣,加上诱人的标题和…

    2022年5月20日
    760
  • 莆田系四大家族是什么,揭秘莆田四大医疗家族名单

    最近莆田系火了,它是何方神圣呢?让小编给大家普及一下。 游走江湖,性病游医起家。以福建莆田为根据地,背着医药包征战中国大江南北。北至佳木斯,南至海南岛。 莆田医帮大哥陈德良带着侄子詹国团、邻居陈金秀、镇党委书记的儿子林志忠、外加“徒弟的徒弟”黄德峰,靠游医敛财,现今成为莆田富豪四大家族陈詹林黄。

    2022年9月3日
    5440
  • 网站设计怎么样好看,网站设计流程及内容

    网站定位设计 网站的定位主要用来确定网站的内容,也就是网站用来做什么。网站的定位通常需要根据市场需求、环境、目标群体等来进行分析和定义。网站定位解决的问题就是网站用来做什么,要提供什么样的服务或者是传达什么样的概念。 1.确定网站类型,做到表里如一 2.确定网站的诉求风格 3.突出网站的卖点。企业花钱建站最重要的目的就是营销,营销要把产品的劣势掩盖把优点凸显出来。只有把卖点凸显出来,才能黏住客户,…

    2022年7月7日
    500
  • dll文件修复软件哪个好(电脑文件修复软件免费)

    计算机dll文件缺失修复工具,一键修复电脑中dll缺失问题。 昨晚网创智慧库分享了;有朋友反馈在使用过程中,遇到了计算机缺失dll的问题,不知道怎么解决。 dll缺失是一个电脑常见问题,在运行某些应用或游戏时,时有发生。 dll文件修复(网图案例) 解决这个问题的方法其实很简单;提示缺失哪个dll文件,就直接在浏览器中搜索该dll文件的名称,下载解压后放入电脑的系统目录即可。 包括常用的电脑管家中…

    2022年5月5日
    1110
  • 小本创业新项目有什么,适合新手的22的创业项目

    什么行业前景好现在是人们经常研究的问题,每个人都想投资几个好项目,这样有可能少奋斗几年了,那么接下来,我们就来给大家分享一下2020年有前景的创业项目有哪些?给想创业的人一些参考。 一、个性读吧 随着社会经济的发展,现代人的物质生活越来越丰富,但是人们的精神生活却越来越匮乏,在这个知识经济时代,活到老、学到老显得愈发重要,因此在闲暇之余读书将会成为人们的不二之选,所以开一家读吧,想必十分有发展前景…

    2022年8月31日
    600
  • 长沙创业网最新政策,如何在长沙创业孵化基地入驻

    为什么湘籍的互联网人的成就要比湖南本土创业者大这么多?长沙目前的互联网情况究竟怎么样?长沙互联网有什么优秀公司?长沙互联网有什么政策?如果你也有这些疑问,那么相信本文一定会非常合你的胃口。 在中国互联网行业,湖南人是一股不容忽视的力量。谈及湘籍互联网大佬,顶尖投资人熊晓鸽、微信之父张小龙、58同城姚劲波、金蝶软件董事长徐少春、清科集团董事长倪正东、陌陌唐岩、映客直播奉佑生、世纪佳缘创始人龚海燕等大…

    2022年5月18日
    1280

发表回复

登录后才能评论

联系我们

400-800-8888

在线咨询: QQ交谈

邮件:admin@example.com

工作时间:周一至周五,9:30-18:30,节假日休息

关注微信