线程池原理相关问题

工作中常用线程池的(通过Executors)创建方法分为以下四种:

newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

查看源码可知,他们底层都是用ThreadPoolExecutor实现的:

ThreadPoolExecutor线程池-->继承AbstractExecutorService类-->实现ExecutorService接口-->继承Executor类

面试中也经常会问线程池的参数问题:

常用的五个参数: new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);

corePoolSize:核心池的大小。创建线程池后,默认池中没有线程,有任务的时候才会去创建(当池中线程数目达到corePoolSize后会把到达的任务放到缓存队列中);

      如果调用prestartAllCoreThreads()或者prestartCoreThread()方法,才会在没有任务的时候就预先创建corePoolSize个线程或者一个线程。

maximumPoolSize:线程池中最大线程数

keepAliveTime:线程没有执行任务时,最多保持多久会终止。默认当线程数量超过corePoolSize时,keepAliveTime起作用,如果调用了allowCoreThreadTimeOut(fasle)时,不管线程数量多少,keepAliveTime都会起作用

unit:keepAliveTime的单位(TimeUnit.DAYS/HOURS/MINUTES/SECONDS/MILLISECONDS/MICROSECONDS/NANOSECONDS)

workQueue:一个阻塞队列,用来存储等待执行的任务,常用的有LinkedBlockingQueue和SynchronousQueue,ArrayBlockingQueue和PriorityBlockingQueue使用较少

public class ThreadPoolDemo {

    public static class MyTask implements Runnable {

        public void run() {
            System.out.println(System.currentTimeMillis() + ":Thread ID:" + Thread.currentThread().getId());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

    public static void main(String[] args) {
        // 当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。
        // 如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了。
     // ThreadPoolExecutor exec = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<>(5));
        // ArrayBlockingQueue(有界阻塞队列--必须指定大小)和PriorityBlockingQueue使用较少,一般使用LinkedBlockingQueue(无界阻塞队列)和Synchronous。线程池的排队策略与WorkQueue有关。
        ThreadPoolExecutor exec = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<Runnable>());
        // 创建一个可根据实际情况调整线程数量线程池
        for (int i = 0; i < 15; i++) {
            MyTask myTask = new MyTask();
            exec.submit(myTask);
            System.err.println("线程池中线程数目:" + exec.getPoolSize() + ",队列中等待执行的任务数:" + exec.getQueue().size()
                    + ",已执行完别的任务数:" + exec.getCompletedTaskCount());
        }
        exec.shutdown();
    }

}

使用new ArrayBlockingQueue<>(5),开启15个线程,输出如下:

使用new ArrayBlockingQueue<>(5),开启20个线程,输出如下:

使用new LinkedBlockingDeque<>(),开启20个线程,输出如下:

 

newFixedThreadPool和newCachedThreadPool有什么区别?

newFixedThreadPool该方法返回一个固定数量的线程池,当一个新任务提交时,线程池中若有空闲线程,则立即执行;

          若没有,则新的任务会被暂存到一个任务队列中,待有空闲线程时再进行处理。

newCachedThreadPool该方法返回一个可根据实际情况调整线程数量的线程池。当一个新任务提交时,若有空闲的线程

          可以复用则优先使用可服用的线程;若没有,则创建新的线程来处理任务。当任何一个线程处理完当前任务后都将返回到线程池中以备复用。

为什么不用newCachedThreadPool?

newCachedThreadPool的maximumPoolSize 最大值Integer.MAX_VALUE,一般来说机器都没有这么大内存供他使用,比如

在JDK1.5以后一个线程的堆栈大小为1M及-Xss1024k如果一下有10000个线程10000*1M=约10G的内存空间供给线程使用,这个是不现实的。

原文地址:https://www.cnblogs.com/java-spring/p/10794299.html