多线程与并发(十):多线程同步模式

* 同步模式之两阶段终止(Two Phase Termination)

在一个线程 t1 如何优雅结束线程 t2 ? 优雅的意思是:给线程t2 一个料理后事的机会

  1. 错误思路
  • 使用线程对象的stop 方法停止线程
    • stop 方法会真正杀死线程,如果这时线程锁住了共享资源,那么当他被杀死后,就再也没有机会释放锁,其他线程将永远无法获取锁
  • 使用System.exit(int)方法停止线程
    • 目的仅是停止一个线程,但这种做法会让整个程序都停止

例如:要做一个系统的健康状况监控,没个两秒记录一次

两阶段终止模式.jpg
public class InterruptModeTest {

    public static void main(String[] args) {
        TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
        twoPhaseTermination.start();

        try {
            Thread.sleep(3500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        twoPhaseTermination.stop();
    }
}
class TwoPhaseTermination{
    private Thread monitor;

    //启动监控线程
    public void start(){
        monitor = new Thread(()->{
            while (true){
                if (Thread.currentThread().isInterrupted()){
                    System.out.println("料理后事");
                    break;
                }

                try {
                    Thread.sleep(1000); //情况一:睡眠过程中被打断
                    System.out.println("执行监控..."); //情况二
                } catch (InterruptedException e) {
                    e.printStackTrace();

                    Thread.currentThread().interrupt(); //重新设置打断标记
                }

            }
        });

        monitor.start();
    }

    //终止线程
    public void stop(){
        monitor.interrupt();
    }
}

执行结果:
执行监控...
执行监控...
执行监控...
java.lang.InterruptedException: sleep interrupted
at java.lang.Thread.sleep(Native Method)
at com.lily.base.TwoPhaseTermination.lambdastart0(InterruptModeTest.java:32)
at java.lang.Thread.run(Thread.java:748)
料理后事


细节一:sleep 时打断
细节二:执行监控记录时打断

同步模式之保护性暂停

即Guarded Suspension, 用在一个线程等待另一个线程的执行结果。

要点

  • 有一个结果需要从一个线程传递到另一个线程,让他们关联同一个GuardedObject

  • 如果有结果不断从一个线程到另一个线程那么可以使用消息队列(见生产者/消费者)

  • JDK中,join 的实现,Future 的实现,采用的就是此模式

  • 因为要等待另一方的结果,因此归类到同步模式


    保护性暂停.jpg
  • 代码实现

保护性暂停-扩展-增加超时

join方法

看一下join 的源码

保护性暂停-扩展-解耦等待和生产-分析

图中Futures就好比居民楼一层的信箱(每个信箱有房间编号),左侧的t0,t2,t4就好比等待邮件的居民,右侧t1,t3,t5就好比邮递员。

如果需要在多个类之间使用GuardedObject 对象,作为参数传递不是很方便,因此设计一个用来解耦的中间类,这样不仅能够解耦【结果等待者】和【结果生产者】,还能够同时支持多个任务的管理。

异步模式之工作线程

  1. 定义
    让有限的工作线程来轮流异步处理无限多的任务。也可以将其归类为分工模式,它的典型实现就是线程池,也体现了经典设计模式中的享元模式。

例如,海底捞的服务员(线程),轮流处理每位客人的点餐(任务),如果每位客人都配备一名专属的服务员,那么成本就太高了(对比另一种多线程设计模式:Thread-Per-Message)

注意,不同任务类型应该使用不同的线程池,这样能够避免饥饿,并能提升效率

例如,如果一个餐馆的工人既要招呼客人,又要到后厨做菜显然效率不高,分成服务员(线程池A),厨师(线程池B)更为合理,当然你能想到更细致的分工。

2.饥饿

固定大小线程池会有饥饿现象

  • 两个工人是同一个线程池的两个线程
  • 他们要做的事是:为客人点餐和到后厨做菜,这时两个阶段的工作。
    • 客人点餐:必须先点完餐,等菜做好,上菜,在此期间处理点餐的工人必须等待
    • 后厨做菜:没啥说的,做就是了
  • 比如工人A处理了点餐任务,接下来它要等着工人B把菜做好,然后上菜,他两也配合的蛮好的
  • 但现在同时来了两个客人,这个时候工人A 和 工人B 都去处理点餐了,这时没人做菜了,死锁
public class TestDeadLock {

    static final List<String> MENU = Arrays.asList("地三鲜","宫保鸡丁","辣子鸡丁","烤翅");
    static Random RANDOM = new Random();
    static String cooking(){
        return MENU.get(RANDOM.nextInt(MENU.size()));
    }

    public static void main(String[] args) throws InterruptedException{
        ExecutorService pool = Executors.newFixedThreadPool(2);

        pool.execute(()->{
            System.out.println("处理点餐...");

            Future<String> f = pool.submit(()->{
                System.out.println("做菜");
                return cooking();
            });

            try {
                System.out.println("上菜 " + f.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }

        });

        pool.execute(()->{
            System.out.println("处理点餐...");

            Future<String> f = pool.submit(()->{
                System.out.println("做菜");
                return cooking();
            });

            try {
                System.out.println("上菜 " + f.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }

        });
    }
}

执行结果:
处理点餐...
处理点餐...


饥饿解决
不同的任务类型使用不同的线程池

public class TestDeadLock {

   static final List<String> MENU = Arrays.asList("地三鲜","宫保鸡丁","辣子鸡丁","烤翅");
   static Random RANDOM = new Random();
   static String cooking(){
       return MENU.get(RANDOM.nextInt(MENU.size()));
   }

   public static void main(String[] args) throws InterruptedException{
       ExecutorService orderPool = Executors.newFixedThreadPool(2);
       ExecutorService cookPool = Executors.newFixedThreadPool(2);

       orderPool.execute(()->{
           System.out.println(Thread.currentThread().getName() + " 处理点餐...");

           Future<String> f = cookPool.submit(()->{
               System.out.println(Thread.currentThread().getName() + " 做菜");
               return cooking();
           });

           try {
               System.out.println(Thread.currentThread().getName() + " 上菜 " + f.get());
           } catch (InterruptedException | ExecutionException e) {
               e.printStackTrace();
           }

       });

       orderPool.execute(()->{
           System.out.println(Thread.currentThread().getName() + " 处理点餐...");

           Future<String> f = cookPool.submit(()->{
               System.out.println(Thread.currentThread().getName() + " 做菜");
               return cooking();
           });

           try {
               System.out.println(Thread.currentThread().getName() +" 上菜 " + f.get());
           } catch (InterruptedException | ExecutionException e) {
               e.printStackTrace();
           }

       });
   }
}

执行结果:
pool-1-thread-1 处理点餐...
pool-1-thread-2 处理点餐...
pool-2-thread-1 做菜
pool-1-thread-2 上菜 烤翅
pool-2-thread-2 做菜
pool-1-thread-1 上菜 辣子鸡丁


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

推荐阅读更多精彩内容