《Java并发编程实战》31到35讲学习总结

第31讲心得

该讲介绍了Guarded Suspension模式。

  1. 一个对象 GuardedObject,内部有一个成员变量——受保护的对象,以及两个成员方法——get(Predicate<T> p)和onChanged(T obj)方法。其中get() 方法用来实现等待,参数 p 就是用来描述这个前提条件的;onChanged() 方法可以 fire 一个事件,而这个事件往往能改变前提条件 p 的计算结果。
    
    class GuardedObject<T>{
      //受保护的对象
      T obj;
      final Lock lock = 
        new ReentrantLock();
      final Condition done =
        lock.newCondition();
      final int timeout=2;
      //保存所有GuardedObject
      final static Map<Object, GuardedObject> 
      gos=new ConcurrentHashMap<>();
      //静态方法创建GuardedObject
      static <K> GuardedObject 
          create(K key){
        GuardedObject go=new GuardedObject();
        gos.put(key, go);
        return go;
      }
      static <K, T> void 
          fireEvent(K key, T obj){
        GuardedObject go=gos.remove(key);
        if (go != null){
          go.onChanged(obj);
        }
      }
      //获取受保护对象  
      T get(Predicate<T> p) {
        lock.lock();
        try {
          //MESA管程推荐写法
          while(!p.test(obj)){
            done.await(timeout, 
              TimeUnit.SECONDS);
          }
        }catch(InterruptedException e){
          throw new RuntimeException(e);
        }finally{
          lock.unlock();
        }
        //返回非空的受保护对象
        return obj;
      }
      //事件通知方法
      void onChanged(T obj) {
        lock.lock();
        try {
          this.obj = obj;
          done.signalAll();
        } finally {
          lock.unlock();
        }
      }
    }

     

第33讲心得

该讲介绍了Thread-Per-Message模式。

  1. Thread-Per-Message 模式,简言之就是为每个任务分配一个独立的线程。最经典的应用场景是网络编程里服务端的实现,服务端为每个客户端请求创建一个独立的线程,当线程处理完请求后,自动销毁,这是一种最简单的并发处理网络请求的方法。
  2.  Thread-Per-Message 模式如果使用线程池方案就会增加复杂度。Thread-Per-Message 模式在 Java 领域并不是那么知名,根本原因在于 Java 语言里的线程是一个重量级的对象,为每一个任务创建一个线程成本太高,尤其是在高并发领域,基本就不具备可行性。
  3. 对于一些并发度没那么高的异步场景,例如定时任务,采用 Thread-Per-Message 模式是完全没有问题的。

第34讲心得

该讲介绍了 Worker Thread 模式。

  1. 如何去实现 Worker Thread 模式呢?你很容易就能想到用阻塞队列做任务池,然后创建固定数量的线程消费阻塞队列中的任务。其实你仔细想会发现,这个方案就是 Java 语言提供的线程池。
  2. 线程池有很多优点,例如能够避免重复创建、销毁线程,同时能够限制创建线程的上限等等。
  3. Java 的线程池既能够避免无限制地创建线程导致 OOM,也能避免无限制地接收任务导致 OOM。只不过后者经常容易被我们忽略。所以强烈建议你用创建有界的队列来接收任务。当请求量大于有界队列的容量时,就需要合理地拒绝请求。如何合理地拒绝呢?这需要你结合具体的业务场景来制定,即便线程池默认的拒绝策略能够满足你的需求,也同样建议你在创建线程池时,清晰地指明拒绝策略。
  4. 使用线程池过程中,还要注意一种线程死锁的场景。如果提交到相同线程池的任务不是相互独立的,而是有依赖关系的,那么就有可能导致线程死锁。。这种问题通用的解决方案是为不同的任务创建不同的线程池。最后再次强调一下:提交到相同线程池中的任务一定是相互独立的,否则就一定要慎重。

第35讲心得

本讲介绍了两阶段终止模式。

  1. Java 语言的 Thread 类中曾经提供了一个 stop() 方法,用来终止线程,可是早已不建议使用了,原因是这个方法用的就是一剑封喉的做法,被终止的线程没有机会料理后事。
  2. 前辈们经过认真对比分析,已经总结出了一套成熟的方案,叫做两阶段终止模式。顾名思义,就是将终止过程分成两个阶段,其中第一个阶段主要是线程 T1 向线程 T2发送终止指令,而第二阶段则是线程 T2响应终止指令。
  3. Java 线程进入终止状态的前提是线程进入 RUNNABLE 状态,而实际上线程也可能处在休眠状态,也就是说,我们要想终止一个线程,首先要把线程的状态从休眠状态转换到 RUNNABLE 状态。如何做到呢?这个要靠 Java Thread 类提供的 interrupt() 方法,它可以将休眠状态的线程转换到 RUNNABLE 状态。
  4. 线程转换到 RUNNABLE 状态之后,我们如何再将其终止呢?RUNNABLE 状态转换到终止状态,优雅的方式是让 Java 线程自己执行完 run() 方法,所以一般我们采用的方法是设置一个标志位,然后线程会在合适的时机检查这个标志位,如果发现符合终止条件,则自动退出 run() 方法。这个过程其实就是我们前面提到的第二阶段:响应终止指令。

  5. 综合上面这两点,我们能总结出终止指令,其实包括两方面内容:interrupt() 方法和线程终止的标志位。

  6. 按照两阶段终止模式,我们首先需要做的就是将线程 rptThread 状态转换到 RUNNABLE,做法很简单,只需要在调用 rptThread.interrupt() 就可以了。线程 rptThread 的状态转换到 RUNNABLE 之后,如何优雅地终止呢?下面的示例代码中,我们选择的标志位是线程的中断状态:Thread.currentThread().isInterrupted() ,需要注意的是,我们在捕获 Thread.sleep() 的中断异常之后,通过 Thread.currentThread().interrupt() 重新设置了线程的中断状态,因为 JVM 的异常处理会清除线程的中断状态。

    
    class Proxy {
      boolean started = false;
      //采集线程
      Thread rptThread;
      //启动采集功能
      synchronized void start(){
        //不允许同时启动多个采集线程
        if (started) {
          return;
        }
        started = true;
        rptThread = new Thread(()->{
          while (!Thread.currentThread().isInterrupted()){
            //省略采集、回传实现
            report();
            //每隔两秒钟采集、回传一次数据
            try {
              Thread.sleep(2000);
            } catch (InterruptedException e){
              //重新设置线程中断状态
              Thread.currentThread().interrupt();
            }
          }
          //执行到此处说明线程马上终止
          started = false;
        });
        rptThread.start();
      }
      //终止采集功能
      synchronized void stop(){
        rptThread.interrupt();
      }
    }

     

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值