• coolrandy
    2019-05-23
    老师好 能不能后面讲一讲分布式锁相关的东西,比如实现方案,原理和场景之类的
    
     15
  • 拯救地球好累
    2019-08-10
    并发设计模式是前人在做并发编程时已经归纳好的,在不同场景下具有可行性的设计模式,我们在设计并发程序时应当优先考虑这些设计模式(以及这些设计模式的组合)。
    对各类并发设计模式,考量其核心思想、核心技术、trade-off、适用场景、与其他设计模式的对比等。
    首先,应当考虑没有共享的模式,这类方式用一些技术手段来避免并发编程中需要考虑的同步、互斥等问题,有些模式的实现也被称为无锁机制,其简单且不易出错。
    Immutability模式充分利用了面向对象的封装特性,将类的mutator的入口全部取消,自身的状态仅允许创建时设置。状态的改变通常通过新建一个对象来达成,为了避免频繁创建新对象,通常通过享元模式或对象池来解决该问题。因此,其适用于对象状态较少改变或不变的场景,需要对一定的内存overhead可容忍。
    COW模式通过写时拷贝的方式保证读取时候的无阻塞及多线程读写时的无共享,由于其写入时的拷贝机制和加锁机制(JAVA中),因此仅适合于读多写非常少的场景。相比于Immutability模式,COW将引用指向新对象的操作封装在了内部(JAVA中)来实现一定的可变性。
    线程本地存储模式利用线程本地存储空间(TLAB)来存储线程级别的对象以保证各线程操作对象的隔离性,一定程度上可以等同于能够携带上下文信息的局部变量。JAVA中是在用户空间实现的ThreadLocal控制的,目前的实现可以保证map的生命周期与各Thread绑定,但Value需要我们手动remove来避免内存泄漏。
    其次,从分工、同步、互斥三个角度来看几个设计模式。
    从分工的角度看,以下三种模式在对线程工作粒度的划分上逐渐变细。
    Thread-per-message模式通过一消息/请求一线程的方式处理消息/请求,这种模式要求线程创建/销毁overhead低且线程占用内存的overhead也低,因此在overhead高时需要保证线程的数量不多,或者采用更轻量级的线程(如协程)来保证。
    Worker Thread模式相当于在Thread-per-message模式的基础上让消息/请求与threads的工厂打交道,在JAVA中可以理解为线程池,通过将同类消息/请求聚类到某类工厂(也有工厂模式的意思在)来为这类消息/请求提供统一的服务(定量的线程数、统一的创建方法、统一的出错处理等),当然,它依然有Thread-per-message中需要控制线程占用内存的问题。
    生产者-消费者模式在Woker Thread模式的基础上加入了对消息/请求的控制(大部分使用队列来控制),并划定了生产者线程和消费者线程,其中它也包含了同步和互斥的设计,在JAVA中的线程池中也可见一斑。这类设计常见于MQ中。
    从同步和互斥的角度看,多线程版本的if被划分为了两种模式(Guarded Suspension模式和Balking模式)。
    Guarded Suspension模式是传统的等待-通知机制的实现,非常标准化,JAVA中则依赖管程实现了各种工具类来保证多线程版本if的正确性。
    Balking模式依赖于互斥保证多线程版本if的正确性。
    两阶段终止模式在线程粒度的管理中通过中断操作和置位标记来保证正常终止,JAVA中在线程池粒度的管理中可以通过SHUNDOWN方法来对线程池进行操作,源码中可以看到,其实质也是通过第一种方式来达成目的的。
    展开

    作者回复: 👍

    
     6
  • 青莲
    2019-05-25
    老师想请问下,如果jvm挂了,有没有好的办法能记录下线程池当前未处理的任务

    作者回复: 没有好的办法,可以通过分布式来解决,把未处理的任务先放到数据库里,处理完从数据库删除

    
     5
  • PJ ◕‿◕
    2019-05-23
    老师好 能不能后面讲一讲分布式锁相关的东西,比如实现方案,原理和场景之类的

    作者回复: 方案就是利用zk,redis,db,也可以用atomix这样的工具类自己做集群管理,网上有很多资料,最近实在太忙了😂😂😂

    
     3
  • null
    2019-06-15
    老师,您好!
    我有一个批跑任务,第一次调用 start() 方法启动任务,当任务跑完后,调用 stop() 方法,正常退出线程池。
    当下一次再调用 start() 方法启动任务时,报:
    java.util.concurrent.RejectedExecutionException: com.xxx.LoggerService$$Lambda$12/690901601@72f8abb rejected from java.util.concurrent.ThreadPoolExecutor@9e8742e[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 1] 错误位置:ThreadPoolExecutor.java:2047

    请问老师,当每次任务运行完毕之后,我想正常退出线程池,也希望下一次运行时,能继续正常运行,该如何做呢?
    谢谢老师

    下面是 demo:
    @Service
    public class LoggerService {

      // 用于终止日志执行的“毒丸”
      final LogMsg poisonPill = new LogMsg(LEVEL.ERROR, "");

      // 任务队列
      final BlockingQueue<LogMsg> bq = new LinkedBlockingQueue<>();

      // 只需要一个线程写日志
      ExecutorService es = Executors.newFixedThreadPool(1);

      // 启动写日志线程
      public void start() {
        System.out.println("启动日志服务");

        this.es.execute(() -> {
          try {
            while (true) {
              System.out.println("获取日志内容");
              LogMsg log = bq.poll(5, TimeUnit.SECONDS);
              // 如果是“毒丸”,终止执行
              if (poisonPill.equals(log)) {
                  break;
              }
              // 省略执行逻辑
            }
          } catch (Exception e) {
          } finally {

          }
        });
      }

      // 终止写日志线程
      public void stop() {
        System.out.println("关闭日志服务");
        // 将“毒丸”对象加入阻塞队列
        bq.add(poisonPill);
        es.shutdown();
      }

      // 日志级别
      enum LEVEL {
        INFO, ERROR
      }

      class LogMsg {
        LEVEL level;
        String msg;

        // 省略构造函数实现
        LogMsg(LEVEL lvl, String msg) {
        }
        // 省略 toString() 实现
      }

    }
    展开

    作者回复: 下次运行时重建线程池。你关闭线程池的原因是什么?

    
     1
  • gogo
    2019-06-10
    青莲同学,当老师说没有好的办法的时候,不知道为啥,我总想笑😂😂😂
    
     1
  • aoe
    2019-11-19
    老师推荐的书都挺好
    
    
  • null
    2019-08-23
    老师,您好!
    文章示例中,使用毒丸对象终止线程的场景是单线程。
    如果是多线程的情况,如何也让其余线程优雅退出呢?
    谢谢老师

    作者回复: 《Java并发编程实战》里有详细的介绍,你可以参考一下

    
    
  • gogo
    2019-07-09
    喜欢宝令老师的专栏

    作者回复: 😄

     1
    
  • null
    2019-06-15
    作者: 下次运行时重建线程池。你关闭线程池的原因是什么?

    谢谢老师回复!!
    每天凌晨跑结算数据,每天只跑一次,就想着跑完任务之后,关闭线程池,这样就不会再占用服务器资源了。

    作者回复: 这种情况可能没必要用线程池,如果需要,可以设置合适的corepoolsize和keepalivetime,也可以重建

    
    
  • 缪文@场景鹿
    2019-05-23
    毒丸对象,我也用过,就是一个可以通过外部接口或消息通知还写的bean,需要终止时设置为终止状态,不终止时是正常状态,消费线程在读到终止状态时直接跳过任务执行,线程也就完成终止了

    作者回复: 👍

    
    
  • 强哥
    2019-05-23
    很期待接下来两个模块的深入讲解!
    
    
  • 张三
    2019-05-23
    打卡!
    
    
我们在线,来聊聊吧