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

2021-09-10  本文已影响0人  lilykeke

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

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

  1. 错误思路

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

两阶段终止模式.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, 用在一个线程等待另一个线程的执行结果。

要点

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

join方法

看一下join 的源码

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

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

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

异步模式之工作线程

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

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

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

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

2.饥饿

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

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 上菜 辣子鸡丁


上一篇 下一篇

猜你喜欢

热点阅读