1.为什么要用线程池

  第一是减少线程创建销毁的次数,利用线程池可以让一个线程多次使用

  第二是提高响应的速度,因为可以直接拿线程池里面的线程使用,减少了创建的过程(当然加入线程池里有空闲线程)

  第三是便于管理

2.线程池初试

  一般是通过ThreadPoolExecutor构造函数来创建线程池,但是还可以通过FixedThreadPool(固定线程数);SingleThreadExecutor(单一线程,队列先进先出),这2中允许的队列长度无限,可能会堆积大量的待运行任务;CachedThreadPool(无队列,当能够复用的时候后会优先复用线程)因为没有队列,有可能造成大量的线程被创建。所以一般通过ThreadPoolExecutor来创建线程。

public ThreadPoolExecutor(
int corePoolSize,//线程池的核心线程数量
int maximumPoolSize,//线程池的最大线程数
long keepAliveTime,//当线程数大于核心线程数时,多余的空闲线程存活的最长时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//任务队列,用来储存等待执行任务的队列
ThreadFactory threadFactory,//线程工厂,用来创建线程,一般默认即可
RejectedExecutionHandler handler//拒绝策略,当提交的任务过多而不能及时处理时,我们可以定制策略来处理任务
){ this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, Executors.defaultThreadFactory(), defaultHandler); }

  可以发现定义了线程核心数量,线程池最大数量,存活时间,时间单位,等待执行任务的队列,线程工厂,拒绝策略.

  创建线程池后,内部在运行线程为0,当一个任务需要执行的时候,先判断,如果当前线程数小于Core线程数,创建新的线程去运行,反之放入队列,等待运行,如果队列满了,检查当前线程数与最大线程数,如果当前线程数小线程数,创建新线程去运行,如果线程数大于等于最大线程数,则会报异常。可以看出,线程池能接收的最大任务数为最大线程数量+队列数量。

  当定义线程池后,线程数是0,可以通过prestartAllCoreThreads/preStartCoreThread方法来预热线程池,不同的是前者会创建Core数量的线程,而后面一个则只会创建一个。

  存活时间,主要作用与核心线程之外的线程,当非核心线程空闲该时间后,就会结束。不过 可以通过allowsCoreThreadTimeOut()方法可以让核心线程也会结束。

  经常遇到的问题是corePoolSize/maximumPoolSize初始化时设置大小的问题。

  正经的答案是看这个线程池是面向计算还是面向io的。当io比较多的时候,一般会设置cpu核心线程/(1-阻塞系数) (一般是0.8~0.9),当面向计算的时候,一般是设置成为cpu核心线程*2。

  那么不怎么正常的答案便是,通过setCorePoolSize/setMaximumPoolSize方法对以及创建的线程池进行动态修改。

  首先setCorePoolSize先对参数进行检查,delta是设置数减当前核心线程数的值,覆盖原来的核心线程数。

    public void setCorePoolSize(int corePoolSize) {
        if (corePoolSize < 0 || maximumPoolSize < corePoolSize)
            throw new IllegalArgumentException();
        int delta = corePoolSize - this.corePoolSize;
        this.corePoolSize = corePoolSize;
      //当核心线程数小于当前运行的线程数,多的停掉
if (workerCountOf(ctl.get()) > corePoolSize) interruptIdleWorkers();
      //当设置的值比原来的大
else if (delta > 0) { // We don't really know how many new threads are "needed". // As a heuristic, prestart enough new workers (up to new // core size) to handle the current number of tasks in // queue, but stop if queue becomes empty while doing so.
        //k为核心数差值与队列大小最小数
int k = Math.min(delta, workQueue.size()); while (k-- > 0 && addWorker(null, true)) {
        //一直创建新的线程,直到队列为空或者当前运行线程数==设置后的核心线程数
if (workQueue.isEmpty()) break; } } }


    public void setMaximumPoolSize(int maximumPoolSize) {
      //参数检查
if (maximumPoolSize <= 0 || maximumPoolSize < corePoolSize) throw new IllegalArgumentException();
      //覆盖
this.maximumPoolSize = maximumPoolSize;
      //当前运行的线程数大于设置后的值,多余的线程中断
if (workerCountOf(ctl.get()) > maximumPoolSize) interruptIdleWorkers(); }

 

  不过用这种方法设置的时候需要把Core和Max设置成一样的参数。

  详见:https://mp.weixin.qq.com/s?__biz=Mzg3NjU3NTkwMQ==&mid=2247505103&idx=1&sn=a041dbec689cec4f1bbc99220baa7219&source=41#wechat_redirect

  最后一个参数是拒绝策略:

  默认的拒绝策略是AbortPolicy,即当工作线程数==最大线程数且队列满了,当有一个新的任务到来,就会拒绝,丢弃任务并抛出RejectedExecutionException异常(滚)

  DiscardPolicy策略,和AbortPolicy区别在于,不会抛出异常(不理那种,比滚更狠)

  DiscardOldestPolicy策略,丢弃队列最前面的任务,然后重新提交被拒绝的任务。(喜新厌旧)

  CallerRunsPolicy策略,由调用该任务的线程去运行(别哭)

  

import java.util.Date;

public class MyRunnable implements Runnable {
     private String command;

        public MyRunnable(String s) {
            this.command = s;
        }

        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName() + " Start. Time = " + new Date());
            processCommand();
            System.out.println(Thread.currentThread().getName() + " End. Time = " + new Date());
        }

        private void processCommand() {
            try {
                Thread.sleep(5000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        @Override
        public String toString() {
            return this.command;
        }
}
public class ThreadPoolExecutorDemo {

    private static final int CORE_POOL_SIZE = 5;
    private static final int MAX_POOL_SIZE = 10;
    private static final int QUEUE_CAPACITY = 100;
    private static final Long KEEP_ALIVE_TIME = 1L;
    public static void main(String[] args) throws InterruptedException, ExecutionException {

        //使用阿里巴巴推荐的创建线程池的方式
        //通过ThreadPoolExecutor构造函数自定义参数创建
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                CORE_POOL_SIZE,
                MAX_POOL_SIZE,
                KEEP_ALIVE_TIME,
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(QUEUE_CAPACITY),
                new ThreadPoolExecutor.CallerRunsPolicy());
        for (int i = 0; i < 10; i++) {
            //创建WorkerThread对象(WorkerThread类实现了Runnable 接口)
            Runnable worker = new MyRunnable("" + i);
            //执行Runnable:这里还有一种提交任务的方法submit,区别在于submit会返回Future对象
            executor.execute(worker);
        }
        //终止线程池
        executor.shutdown();
        while (!executor.isTerminated()) {
        }
        System.out.println("Finished all threads");
        //重新设置核心线程数
        executor.setCorePoolSize(9);
        System.out.println(executor.getCorePoolSize());
        //重新设置最大线程数
        executor.setMaximumPoolSize(20);
        System.out.println(executor.getMaximumPoolSize());
    }
}

 

  

posted on 2021-04-19 16:50  Refuse_to_be_simple  阅读(87)  评论(0编辑  收藏  举报