并发——线程池

2019-07-12  本文已影响0人  四喜汤圆

一、作用

①降低系统资源消耗,通过重用已存在的线程,降低线程创建和销毁造成的消耗;
②提高系统响应速度,当有任务到达时,无需等待新线程的创建便能立即执行;
③方便线程并发数的管控,线程若是无限制的创建,不仅会额外消耗大量系统资源,更是占用过多资源而阻塞系统或oom等状况,从而降低系统的稳定性。线程池能有效管控线程,统一分配、调优,提供资源使用率;
④管理线程。线程池提供了定时、定期以及可控线程数等功能的线程池,使用方便简单。

二、相关概念

1. 线程池相关配置

和线程池相关的类ThreadPoolExecutor,该类的构造函数的配置。
(1)int corePoolSize
核心线程数。

新建线程时,若当前线程数 x [0,corePoolSize),新建的是核心线程;若[corePoolSize,+~),新建的是非核心线程。

核心线程默认情况下会一直存活在线程池中,即使这个核心线程啥也不干。
如果指定ThreadPoolExecutorallowCoreThreadTimeOut=true,那么核心线程如果闲置超过一定时间(时长可由参数配置),就会被销毁掉。

(2)int maximumPoolSize
线程池中线程总数最大值(线程总数=核心线程数+非核心)

(3)long keepAliveTime
该线程池中非核心线程闲置超时时长。

若一个非核心线程闲置状态超过设定时长,就会被销毁掉。如果设置allowCoreThreadTimeOut=true,该参数则会作用于核心线程。

(4)TimeUnit unit
keepAliveTime 的单位,TimeUnit 是一个枚举类型,包括:
NANOSECONDS:1微毫秒
MICROSECONDS:1微妙
MILLISECONDS:1毫秒
SECONDS:1秒
MINUTES:1分钟
HOURS:1小时
DAYS:1天

(5)BlockingQueue workQueue
线程池中的任务队列:维护者等待执行的 Runnable 对象。

所有的线程都在干活时,新添加的任务就会被添加到这个队列中等待出来,如果队列满了,则新建非核心线程执行任务。

为了保证不会出现『线程数达到了 maximumPoolSize』的错误,使用这个类型队列时,maximumPoolSize=Integer.MAX_VALUE

区别

2. 线程池执行任务时遵循的规则

(1)如果线程池中线程数量未达到核心线程数量,那么直接启动一个核心线程来执行任务。
(2)如果线程池中线程数量已达到核心线程数量,那么任务会被插入到任务队列中排队等待执行。
(3)如果(2)中任务无法插入到队列中,这往往是由于任务队列已满,这个时候如果线程数量未达到线程池规定的最大值,那么会立刻启动一个非核心线程来执行任务。
(4)如果(3)中线程数量已达到线程池规定的最大值,那么就拒绝执行此任务,ThreadPoolExecutor 会调用 RejectedExecutionHandler 的 rejectedExecution 方法来通知调用者。

【一篇能够帮你理清线程池的文章】https://mp.weixin.qq.com/s/8OPonUkTbHwRI8mrMig58A

3. 常见线程池

通过 Executors 可以创建四类线程池。

(1)FiexThreadPool

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

(2)CachedThreadPool

public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
        60 L, TimeUnit.SECONDS,
        new SynchronousQueue < Runnable > ());
}

当线程池中线程都处于活动状态时,线程池会创建新的线程处理任务,否则利用空闲线程来处理新任务。

(3)ScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
        DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
        new DelayedWorkQueue());
}

(4)SingleThreadExecutor

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

三、使用

1. 通过 ThreadPoolExecutor 创建一个线程池

(1)创建线程

// 新建线程池
ExecutorService service = new ThreadPoolExecutor(5, 10, 10, TimeUnit.HOURS, new LinkedBlockingDeque < Runnable > ());

(2)提交任务
execute()submit()两种方法。

// 使用execute向线程池提交任务
service.execute(new Runnable() {
    public void run() {

    }
});

// 使用submit提交任务
Future < Integer > future = service.submit(new Callable < Integer > () {
    @Override
    public Integer call() throws Exception {
        return 2;
    }
});
try {
    Integer num = future.get();
} catch (ExecutionException e) {
    e.printStackTrace();
} catch (InterruptedException e) {
    e.printStackTrace();
}

(3)线程池关闭

// 关闭线程池
service.shutdown();

service.shutdownNow();

2. 直接使用现有的4种线程池类创建线程池

ExecutorService tp = Executors.newFixedThreadPool(4);
ExecutorService tp2 = Executors.newCachedThreadPool();
ExecutorService tp3 = Executors.newScheduledThreadPool(4);
ExecutorService tp4 = Executors.newSingleThreadExecutor();

3.建议:为线程池命名

阿里规约

四、重点解析

1.Q:线程池中核心线程是如何复用的

陈小缘

2.Q:自己写线程池

参考文献

lrh_Java线程池
线程池,这一篇或许就够了

上一篇 下一篇

猜你喜欢

热点阅读