Java线程池

public ThreadPoolExecutor(int corePoolSize,
                          int maximumPoolSize,
                          long keepAliveTime,
                          TimeUnit unit,
                          BlockingQueue<Runnable> workQueue,
                          RejectedExecutionHandler handler) {
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), handler);
}

构造函数中的几个变量意义:

1 corePoolSize >= 0 线程池中核心线程数量, 这些线程时不会回收的

2 maximumPoolSize  >= corePoolSize 线程池中最大线程数量,非核心线程 在执行任务之后,如果到达空闲时间则销毁

3 keepAliveTime 非核心线程执行结束之后,多长时间后将其销毁,如果为0,则永远不会销毁,

4 workQueue >0: 由于存储新的任务

RejectedExecutionHandler: 任务队列已满,且达到最大核心线程数时的 拒绝策略:abortPolicy(抛出异常),

DiscardPolicy(直接丢弃当前任务),DiscardOldesPolicy(丢弃最老的任务)

线程池中执行线程的顺序:

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);
}

从代码可以看到:

1 如果当前工作线程数量小于核心线程数,创建新线程 执行任务

2 如果当前工作线程数大于等于核心线程,则将新的任务存储到等待队列中

3 如果等待队列已满,且为达到最大线程数,则从任务队列中取出任务并创建非核心线程执行任务

4 如果任务队列已满,且达到最大线程数,则执行拒绝策略

 

java 中提供了一些创建线程池的Api

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

 

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

上面两个时创建固定线程数量的线程池以及单一线程的线程池,它们的特点的corePoolSize == maxPollSize,且keepAliveTime = 0,即线程空闲后,也不会销毁

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

创建Cached线程池,corePoolSize 为0 ,maxPoolSize为Integer.MAX_VALUE,线程空闲后的存活时间为1个小时

/**
 * Creates a thread pool that can schedule commands to run after a
 * given delay, or to execute periodically.
 * @param corePoolSize the number of threads to keep in the pool,
 * even if they are idle
 * @return a newly created scheduled thread pool
 * @throws IllegalArgumentException if {@code corePoolSize < 0}
 */
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
    super(corePoolSize, Integer.MAX_VALUE,
          10L, MILLISECONDS,
          new DelayedWorkQueue());
}

 创建定时发送命令的线程池

 

1. 缓冲队列BlockingQueue简介:

          BlockingQueue是双缓冲队列。BlockingQueue内部使用两条队列,允许两个线程同时向队列一个存储,一个取出操作。在保证并发安全的同时,提高了队列的存取效率。

2. 常用的几种BlockingQueue:

  • ArrayBlockingQueue(int i):规定大小的BlockingQueue,其构造必须指定大小。其所含的对象是FIFO顺序排序的。

  • LinkedBlockingQueue()或者(int i):大小不固定的BlockingQueue,若其构造时指定大小,生成的BlockingQueue有大小限制,不指定大小,其大小有Integer.MAX_VALUE来决定。其所含的对象是FIFO顺序排序的。

  • 一把锁,两个condition,notFull(存操作时,如果满了,需要block,等待notfull信号释放) ,notEmpty(取操作时,如果队列无数组,则block,等待notEmpty操作释放)

  • PriorityBlockingQueue()或者(int i):类似于LinkedBlockingQueue,但是其所含对象的排序不是FIFO,而是依据对象的自然顺序或者构造函数的Comparator决定。

  • SynchronizedQueue():特殊的BlockingQueue,对其的操作必须是放和取交替完成

3. 自定义线程池(ThreadPoolExecutor和BlockingQueue连用):

     自定义线程池,可以用ThreadPoolExecutor类创建,它有多个构造方法来创建线程池。

    常见的构造函数:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue)

https://blog.csdn.net/sky_100/article/details/75174250

如何选择核心线程数 可参考下面的文章

https://blog.csdn.net/tbdp6411/article/details/78443732

 

一般说来,大家认为线程池的大小经验值应该这样设置:(其中N为CPU的个数),IO密集型应用线程对CPU占用较少,通常设置的核心线程数多一点,计算密集型应用 线程对CPU占用时间很多,为了担心CPU 被占满,不适宜设置太多的线程数。

  • 如果是CPU密集型应用,则线程池大小设置为N+1
  • 如果是IO密集型应用,则线程池大小设置为2N+1
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值