多线程之线程池

237 阅读2分钟

一、线程池介绍及优势

1、介绍

  • 线程池做的工作主要是控制运行的线程的数量,在处理过程中将待执行的任务放到队列中,然后在线程创建后启动这些任务。如果要执行的任务超过了线程池最大线程数量,那么需要等待其他线程执行完,才能够去继续分配线程。

2、优势

主要特点:线程复用;控制最大并发数;管理线程

  • 降低资源消耗。通过重复利用已创建的线程,降低线程创建和销毁的系统消耗
  • 提高响应速度。当任务到达时,任务可以不用等到线程创建而使用已有的线程去执行任务
  • 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进一步的分配、调优和监控

二、ThreadPoolExecutor参数详解

java提供了ThreadPoolExecutor这个类为我们自定义线程池,这个类的构造方法主要有7个参数。详解如下:

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          ThreadFactory threadFactory,
                          RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
}

下面将以银行柜台办理业务的流程说明线程池的的7大参数。

银行柜台情况说明如下:2个固定窗口,3个活动窗口,待客区10个位置

工作流程如下:

  • 2个固定窗口接待客人,
  • 客人多时,先坐在待客区,
  • 如果待客区满了,开设活动窗口,活动窗口接待客人
  • 如果固定窗口、待客区、活动窗口都排满了人,则通知新的客人,下次再来。
  • 如果一段时间内没有客人了,撤掉活动窗口,由固定窗口继续接待客人。
1. corePoolSize:核心线程数,即2个固定窗口
2. maximumPoolSize:最大线程数,即2个固定窗口+3个活动窗口
3. keepAliveTime:空闲线程存活时间
4. TimeUnit:时间单位
5. workQueue:任务排队的队列,即待客区
6. threadFactory:创建线程的工厂
7. RejectedExecutionHandler:拒绝策略,有4种。<1>直接拒绝,返回异常<2>将提交的任务退回给主线程<3>丢弃队列中等待时间最长的任务<4>直接丢弃任务
即固定窗口、待客区、活动窗口都排满了人则通知新的客人,下次再来

三、juc提供的线程池

下面贴出了jdk提供给我们的线程池的源码。其实下面几种线程池都不介意使用,如果真的要使用的话还是自己手动去调用ThreadPoolExecutor的构造方法去

1、newCachedThreadPool

public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, 
                                      2147483647, 
                                      60L, 
                                      TimeUnit.SECONDS, 
                                      new SynchronousQueue());
    }

2、newFixedThreadPool

public static ExecutorService newFixedThreadPool(int var0) {
        return new ThreadPoolExecutor(var0, 
                                      var0, 
                                      0L, 
                                      TimeUnit.MILLISECONDS, 
                                      new LinkedBlockingQueue());
    }

3、newScheduledThreadPool

public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, 
              Integer.MAX_VALUE, 
              0, 
              NANOSECONDS,
              new DelayedWorkQueue());
    }

4、newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 
                                    1,
                                    0L, 
                                    TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

四、确定线程数

1、CPU密集型

如果线程多是用作cpu计算,此时线程可以设置为CPU个数+1。

2、IO密集型

有两种策略:

  1. 线程数为2*CPU个数
  2. CPU个数/(1-因子),因子一般为0.8~0.9之间