Java 里的线程池

【Java 里的线程池】

线程池

池化思想在线程中的实际应用,具体到 Java 中就是

类别说明
ExecutorService定义线程池行为的接口
ThreadPoolExecutor具体的实现类
Executors用于创建线程池的工具类

为什么使用线程池?

  1. 降消耗 - 减少因大量创建和销毁重量级资源耗费的系统资源;
  2. 加限制 - 合理的线程池参数设置可以限制应用所能申请的资源,避免系统资源耗尽。

同样适用于其他各种连接池。

常用线程池

线程池队列类型适用场景
newFixedThreadPoolLinkedBlockingQueue (Integer.MAX_VALUE)CPU 密集,耗时长的任务
newCachedThreadPoolSynchronousQueue大量耗时短的任务
newSingleThreadPoolLinkedBlockingQueue串行执行
newScheduledThreadPoolDelayedWorkQueue周期定时执行

线程池的原理

核心参数

private volatile int corePoolSize;
private volatile int maximumPoolSize;
private final BlockingQueue<Runnable> workQueue;
参数意义
corePoolSize核心线程数(一般也就是最小线程数)
maximumPoolSize最大线程数
workQueue任务队列的长度

添加任务

/**
 * 源码里的注释实在是太清晰了,直接看源码吧
 */
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        if (! isRunning(recheck) && remove(command))
            reject(command);
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    else if (!addWorker(command, false))
        reject(command);
}

RUNNING 状态下,提交一个任务 execute(Runnable command),如果线程池中的线程数小于 corePoolSize 则新建一个线程用于执行此任务,如果线程池中的线程数大于等于 corePoolSize 且小于 maxPoolSize 则任务会被放入 workQueue 中排队等待,如果队列满了,就新建线程执行,如果不仅队列满了,还达到了 maxPoolSize 那么只能执行拒绝策略了。

创建线程池注意事项

在《阿里巴巴 Java 开发手册》中推荐手动使用 ThreadPoolExecutor 创建线程池,而不是直接用 Executors 去创建,理由如下:

FixedThreadPool 和 SingleThreadPool:允许的请求队列长度是 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

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

public LinkedBlockingQueue() {
    // 队列长度为 Integer.MAX_VALUE
    this(Integer.MAX_VALUE);
}

CachedThreadPool:允许的创建线程数量为 Integer.MAX_VALUE,可能会堆积大量的请求,从而导致 OOM。

public static ExecutorService newCachedThreadPool() {
    // 第二个参数 maximumPoolSize 是 Integer.MAX_VALUE
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                    60L, TimeUnit.SECONDS,
                                    new SynchronousQueue<Runnable>());
}

生命周期

五种运行状态

状态说明
RUNNING能接受新任务,并且处理队列中的任务
SHUTDOWN不接受新任务,但是处理队列中的任务
STOP不接受新任务,也不处理队列中的任务,同时终止正在运行的任务
TIDYING所有的任务都被中止, workerCount 是 0,状态转换到 TIDYING,然后运行 terminated() 钩子函数
TERMINATEDterminated() 函数执行完成

表示状态的方式

// 初始值 111_0{29} | 000_0{29} = 111_0{29}
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 用于表示线程数量的位 32 - 3 = 29 位
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程的容量 (1 << 29) - 1,存储在低 29 位中 000_1{29}
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

// 运行状态被存储在高 3 位中,3 位,可表示 2^3 八种状态,用了 5 种
// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS; // 111_0{29}
private static final int SHUTDOWN   =  0 << COUNT_BITS; // 000_0{29}
private static final int STOP       =  1 << COUNT_BITS; // 001_0{29}
private static final int TIDYING    =  2 << COUNT_BITS; // 010_0{29}
private static final int TERMINATED =  3 << COUNT_BITS; // 011_0{29}

// Packing and unpacking ctl
// 获取运行状态, c & 111_0{29}
private static int runStateOf(int c)     { return c & ~CAPACITY; }
// 获取工作线程数 c & 000_1{29}
private static int workerCountOf(int c)  { return c & CAPACITY; }
// 运行状态和工作线程数的组合
private static int ctlOf(int rs, int wc) { return rs | wc; }

/*
 * Bit field accessors that don't require unpacking ctl.
 * These depend on the bit layout and on workerCount being never negative.
 */
private static boolean runStateLessThan(int c, int s) {
    return c < s;
}
private static boolean runStateAtLeast(int c, int s) {
    return c >= s;
}
private static boolean isRunning(int c) {
    return c < SHUTDOWN;
}

拒绝策略

策略说明
CallerRunsPolicy在调用者的线程内执行
AbortPolicy (默认)直接抛出 RejectedExecutionException 拒绝运行
DiscardPolicy静默丢弃,什么也不做
DiscardOldestPolicy丢弃最老的任务,转为执行提交的任务

拒绝策略源码分析

拒绝策略的接口

package java.util.concurrent;

public interface RejectedExecutionHandler {

    /**
     * 拒绝策略执行的动作
     */
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);

}

RejectedExecutionHandler 的实现类

/**
 * 在调用者的线程中执行
 * 如果当前线程池被关闭,则丢弃
 */
public static class CallerRunsPolicy implements RejectedExecutionHandler {

    /**
     * 线程池 e 没有关闭的时候,用调用者的线程执行 r
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            // 直接调用 run() 方法就是在调用者的线程中执行
            r.run();
        }
    }
}

/**
 * 直接抛出 RejectedExecutionException 异常
 */
public static class AbortPolicy implements RejectedExecutionHandler {

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        // 抛出异常,拒绝执行
        throw new RejectedExecutionException("Task " + r.toString() +
                                                " rejected from " +
                                                e.toString());
    }
}

/**
 * 静默丢弃,什么也不做
 */
public static class DiscardPolicy implements RejectedExecutionHandler {

    /**
     * 什么也不做
     */
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}

/**
 * 丢弃最老的任务,提交当前任务
 */
public static class DiscardOldestPolicy implements RejectedExecutionHandler {

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            // 丢弃最老的任务
            e.getQueue().poll();
            // 提交当前任务
            e.execute(r);
        }
    }
}

ThreadPoolExecutor 中的默认拒绝策略

/**
 * 在 ThreadPoolExecutor 中的默认拒绝策略
 *
 * The default rejected execution handler
 */
private static final RejectedExecutionHandler defaultHandler =
    new AbortPolicy();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lixifun

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值