java并发编程5:线程池

1、使用线程池的好处

  • 主要目的:避免对线程频繁创建和销毁带来的性能消耗。

  • 通过一个Demo可以直观感受下 :
    执行同一个功能:往list容器存放10万个元素,将所耗时间进行对比 ->
    不使用线程池:

public class ThreadTest {
    public static void main(String[] args) throws Exception {
        final long start = System.currentTimeMillis();
        final Random random = new Random();
        ArrayList<Integer> list = new ArrayList<>();
        for (int i=0;i<100000;i++){
            Thread thread = new Thread(() -> list.add(random.nextInt()));
            thread.start();
            thread.join();
        }
        System.out.println(list.size()+"----"+(System.currentTimeMillis()-start));
    }
}
image.png
  • 使用线程池:
public class ThreadPoolTest {
    public static void main(String[] args) throws InterruptedException {
        final long start = System.currentTimeMillis();
        final Random random = new Random();
        ArrayList<Integer> list = new ArrayList<>();
        //创建线程池
//        ExecutorService executorService = Executors.newSingleThreadExecutor();
        ExecutorService executorService = Executors.newCachedThreadPool();
//        ExecutorService executorService = Executors.newFixedThreadPool(10);
        for (int i=0;i<100000;i++){
            executorService.submit(() -> {
                list.add(random.nextInt());
            });
        }
        executorService.shutdown();
        executorService.awaitTermination(1,TimeUnit.DAYS);
        System.out.println(list.size()+"----"+(System.currentTimeMillis()-start));
    }
}
image.png

可以看到,执行大任务量使用线程池,对性能有很大的提升。

2、线程池的任务调度原理

image.png

核心线程数占满时,放在工作队列中进行阻塞等待,队列占满,就由非核心线程执行任务,当超过设置的最大线程数时,会有饱和策略处理,简单初步了解4种饱和策略(通过RejectedExecutionHandler类):

  • AbortPolicy: 直接抛出异常
  • CallerRunsPolicy:等待,调用所在线程执行任务
  • DiscardOldestPolicy:丢去队列里最近的一个任务,并执行当前任务
  • DiscardPolicy: 不执行任务,直接丢弃。

3、分类

  • newSingleThreadExecutor : 单线程线程池,只有一个线程串行执行任务,一个个执行队列中任务。
  • newFixedThreadExecutor: 固定大小线程池
  • newCachedThreadPool: 无界线程池。工作线程不够就一直创建,有空闲就复用(60秒存活期)。

4、使用选择

Fixed 是单队列多线程,会有锁竞争的时间开销,如果存在慢请求(比如2s),比较适合,底层实现上使用链表阻塞对列,任务的存和取可以并行执行,这样吞吐量更高。因为是并行,一个线程执行时间较长,还有其他线程等着被使用,不影响这些线程的执行时间。
single 多队列单线程,刚好相反,如果多个慢请求堆积在队列中,显然客户端可能会请求超时。
cached 由于可根据任务量情况大小灵活伸缩,空闲没任务就没有占用内存,适合轻负载服务器。
注意:线程池要单例使用,避免多例创建太多线程池

5、线程池策略

需要进行业务隔离,比如系统中请求有 user模块 、订单模块 ,如果都是在一个线程池,那么显然是所有鸡蛋放一个篮子,一台机器崩,业务全部崩,这就是需要每个模块分别穿件一个线程池的初衷,也是业务隔离的思想体现。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容