【Java并发学习】之线程合作

【Java并发学习】之线程合作

前言

在前面的小节中,我们谈到了线程之间有两种主要的关系,一种是竞争关系,另外一种是合作关系,竞争关系的基本概念以及基本操作我们已经在前面一个小节中有所学习,本小节呢,主要学习的是线程之间的合作,通过协调过多个线程之间的关系,使得多线程能更好地为我们服务,提高性能

线程合作

正如在人类世界中,合作是一种非常常见的事情一样,在程序的时间中,合作也是一件非常常见的事情,多个线程共同合作,完成某一个或者某些任务。然而,合作并非说的那么简单,说要合作就能合作,在一般的情况下,我们在合作的过程中会涉及到非常多而且必要的交流,通过交流,我们可以协调合作的过程,比如说,A在生产物品,然后B负责运输A所产生的物品,那么在这样的一个合作的过程中,由于A和B可能会出现一些速度上的差异问题,毕竟任务的难度不一样嘛,所以就会有一些有趣的处理方式了。

处理方式一 轮询机制

处理的方式之一是称之为轮询的方式,具体的过程如下,当A的速度跟不上B的时候,B就每隔一定的时间,比如1s,就跑过来问一下A,物品生产出来了没有啊;反过来,当B的速度更不上A的时候,A就会每个一定时间过来询问B,处理完了没有啊

从上面的操作过程可以看出,这种处理的方式是不太合理的,很明显,无论是A询问B还是B询问A,当两次询问的时间间隔太短的时候,需要频繁地进行询问,当间隔过长的时候,又会导致出现东西堆积太多的情况,而且询问的过程中,A或者B都会浪费一定的资源,比如体力等,这些是属于没必要的消耗。

所以,无论是现实生活中还是在程序开发过程中我们不建议采用这种方式

处理方式二 挂起-通知机制

在上面的操作中,我们可以看到,通过轮询的方式来实现合作的效率是相对比较低的,所以,有了另外一种方式,挂起-通知机制。

同样是上面的场景,此时,如果A的速度跟不上B的速度,则B选择停下来休息(挂起),而不是去询问A,当A准备好的时候,A就主动通知B,然后B重新进入工作模式,反过来也是同理。

上面这种方式是比较适合计算机的工作的,采用这种方式,意味着,当A的速度跟不上B的时候,B选择挂起,也就意味着B放弃CPU,并且交给调度程序重新调度,注意对比轮询,在轮询的方式中,B一直在忙等,也就是说,B一直在占用着CPU,尽管B没有任务可以做。采用挂起-通知机制,可以使得CPU的利用效率相对比较高,使得资源的利用率比较高

线程合作的具体实现

上面大致了解了轮询与挂起-通知机制的区别之后,接下来,就上面的例子,我们来通过代码来具体实现,从具体的代码中来体会这两者的区别

轮询


// 任务A,负责产生物品
class TaskA implements Runnable{

    private boolean finished;

    public TaskA() {
        finished = false;
    }

    public boolean isFinished() {
        return finished;
    }

    @Override
    public void run() {
        while (true){
            finished = false;
            System.out.println("generating ... ");
            finished = true;
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

// 任务B,负责搬移物品
class TaskB implements Runnable{

    private TaskA taskA;

    public TaskB(TaskA taskA) {
        this.taskA = taskA;
    }

    @Override
    public void run() {
        while (true){
            // 轮询A的完成任务情况
            while (!taskA.isFinished()){
                System.out.println("waiting... ");
            }
            System.out.println("moving ... ");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

从上面代码可以看到,任务B在A没有完成的时候,一直处于空等状态,这是比较消耗资源的

接下来我们来看下基于挂起-通知机制的操作

挂起-通知

在Java中,JDK提供了多种方式用于实现该操作,这里我们先采用最简单的方式wait()notify()

wait()操作,顾名思义,当一个线程需要某种资源,而该资源目前不可用的时候,线程可以调用wait操作,使自己阻塞起来

nofity()操作,唤醒线程,表示某种需要的资源已经完成

操作这两个方法有一些需要注意的地方

  • 通常这两个操作不是在同一个线程中完成,毕竟既然自己阻塞了自己,那么说明自己缺少资源嘛,需要其他人来唤醒自己。
  • 此外,切记,在执行这两个方法的时候,需要注意同步操作,原因在于这两个方法相当于操作信号灯,那么肯定不能同一个信号灯在同一时刻有多个人在处理嘛
  • 而且,进行同步的锁必须跟对应的方法的所属对象相同,这也比较好理解,既然是同步操作,那么肯定是或者自己的锁,然后才能操作自己的信号灯嘛

class TaskA implements Runnable{

    @Override
    public void run() {
        while (true){

            System.out.println("generating ... ");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 同步处理
            synchronized (this){
                notify(); // 通知等待A的对象
            }
        }
    }
}

class TaskB implements Runnable{

    private TaskA taskA;

    public TaskB(TaskA taskA) {
        this.taskA = taskA;
    }

    @Override
    public void run() {
        while (true){
            // 同步处理,这里要注意的是,需要同步的是A对象
            synchronized (taskA){
                try {
                    taskA.wait();// 等待A完成,如果没有获取到,则会挂起当前线程
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("moving ... ");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

可以看到,这里的处理方式跟轮询是不同的,当没有资源A的时候,B会被挂起,而不是在忙等,关于wait和notify的一些细节问题,我们将在以后的小节中学习到

总结

本小节我们主要学习了线程合作的概念,以及两种通用的合作方式,轮询和挂起-通知机制,其中,挂起-通知机制是一种比较好的处理方式,我们在以后的学习中也会碰到这种模式,在典型的生产者消费者模式中,我们会进行更详细的学习。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 221,820评论 6 515
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 94,648评论 3 399
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 168,324评论 0 360
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 59,714评论 1 297
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 68,724评论 6 397
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 52,328评论 1 310
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,897评论 3 421
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,804评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 46,345评论 1 318
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 38,431评论 3 340
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 40,561评论 1 352
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 36,238评论 5 350
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,928评论 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 32,417评论 0 24
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,528评论 1 272
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,983评论 3 376
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 45,573评论 2 359

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,290评论 25 707
  • 1. Java基础部分 基础部分的顺序:基本语法,类相关的语法,内部类的语法,继承相关的语法,异常的语法,线程的语...
    子非鱼_t_阅读 31,664评论 18 399
  • Spring Cloud为开发人员提供了快速构建分布式系统中一些常见模式的工具(例如配置管理,服务发现,断路器,智...
    卡卡罗2017阅读 134,702评论 18 139
  • 他一人踱步在漫山梅花中,在雪堆里留下极浅的印子。清浅的鞋印展现了这个清瘦的男人是如何的温和又和善。 李煜一人攀上一...
    鸡娘阅读 1,853评论 0 0
  • 不输于风, 不输于雨, 不输于贫穷与命运。 愿卿有梦,志不疑; 陋巷菜羹,性不嗔。 栋梁工程,获悉十载。 风雨寒夏...
    乙木々阅读 241评论 0 1