[线程池] ------ 形象的描述线程池,用一个特好记的例子来记忆

线程池

为了减少线程频繁的创建和销毁过程,引入池的概念。
将一些线程先创建好放在线程池中,每次来任务就用池中的线程执行,空闲时池中线程就等待,但不销毁。

原始线程池的创建:
ThreadPoolExecutor executor1 = new ThreadPoolExecutor(入参);
入参有4种方式,分别对上面参数的赋值:threadFactory和handler不写就是用默认的
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, RejectedExecutionHandler handler);
(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);

线程池的主要参数

corePoolSize:核心线程池的大小,默认最开始线程池是没有线程的,只有当有任务来的时候会创建线程,然后这个线程以后就不会销毁了,会一直待在线程池中等待任务。
maximumPoolSize:最大线程数。表示线程池最多可以创建多少个线程,当任务队列放满后还有新任务时,会尝试将线程数提高到最大线程数。
keepAliveTime:非核心线程空闲销毁倒计时,即当线程池中的线程数,大于核心线程数时,针对多出来的线程(就叫非核心线程),如果空闲时间超过这个时间,就销毁。
unit:keepAliveTime参数的单位,可以是:

TimeUnit.DAYS;               //天
TimeUnit.HOURS;             //小时
TimeUnit.MINUTES;           //分钟
TimeUnit.SECONDS;           //秒
TimeUnit.MILLISECONDS;      //毫秒
TimeUnit.MICROSECONDS;      //微妙
TimeUnit.NANOSECONDS;       //纳秒

workQueue:阻塞队列,存放要等待的任务,也叫任务队列。可以是:

ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue

threadFactory:线程工厂,用来创建线程。
handler:拒绝任务的策略,当线程数达到maximumPoolSize,并且workQueue也被任务放满时,再来就会被拒绝,可选的拒绝策略有:

ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。
ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常。
ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)。
ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务。

我们举个实际例子来记忆这些参数(重要)

例子的场景是火车票售票大厅,乘客买票的例子。
火车票售票大厅,往往会开多个窗口来卖票,我们把一个个乘客比作是一个个任务,把一个个售票窗口比作一个个线程

假设某县火车票售票大厅,第一天营业,刚上班,一个窗口也没开,这就是线程池的初始化
这时候来了第一位乘客a,同时唤醒第一个售票窗口A,开始办理业务。
此时马上又来了一个乘客b,窗口A正在被占用,于是又开启第二个窗口B为乘客b办理业务。
我们已知这个火车站就2个正式窗口,相当于corePoolSize=2

那么后面来的人就要开始等了,我们把售票大厅的凳子比作任务队列,比如只有3个凳子,即任务队列最多容纳3个任务
与此同时,前面的乘客ab都没办理完,所以窗口A和窗口B都被占用了,后面又连续来了3个乘客cde,刚好,cde都坐在了凳子上,那么凳子就被坐满了。

此时,fg二位乘客也跟着来了,那么此时2个正式窗口被占,3个凳子也被占,于是火车站管理员临时又加了两个窗口,
这两个临时窗口,就相当于非核心线程,前面那两个AB窗口,就是正式窗口,就是核心线程,总共最多开4个窗口,那么就有maximumPoolSize=4
注意了,这里是等到正式窗口被占完,凳子也被坐满,才开的临时窗口。即核心线程占完,队列填满,才会尝试使用非核心线程做任务。

此时7位乘客,算是把这个售票大厅的资源给用完了,后面又来了乘客h,他就进不去了,要么把他赶出去算是抛个异常,要么他就一直着尝试进去算是重试,等等拒绝他的策略,这就用到了拒绝策略

后来慢慢的,前面的乘客都把票买了,凳子也空了,临时窗口也空闲了,等了1个小时也没有任务了,管理员就把临时窗口又撤销了,只留两个正式窗口。这个1小时就是keepAliveTime=1,单位unit是小时TimeUnit.HOURS

常用的线程池使用方式:

常用的线程池,不用new ThreadPoolExecutor(入参)的方式创建,是用Executors来创建的。

可回收缓存线程池(corePoolSize = 0;maximumPoolSize = Integer.MAX_VALUE;线程空闲后都可以销毁,但要注意设置合适的回收时间)

Executors.newCachedThreadPool();
继续上面的火车站的例子,这个情况就相当于火车站没有正式窗口,全是临时窗口,有人就开临时窗口,没人就关了窗口。

固定线程池(corePoolSize = maximumPoolSize)

Executors.newFixedThreadPool(n);
这个情况相当于没有临时窗口了,但是凳子特别多,即workQueue.size() = Integer.MAX_VALUE ,正式窗口和凳子都满后,再来的人就直接拒绝策略给拒绝了。

单线程线程池(corePoolSize = maximumPoolSize = 1,串行用)

Executors.newSingleThreadExecutor();
这个情况就是只有一个正式窗口,其他的和固定线程池一样。

可调度线程池(支持定时与周期性任务)

Executors.newScheduledThreadPool(n);

实战例子

package com.zs.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import java.util.concurrent.*;

/**
 * @author zhangshuai
 **/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:spring-config-test.xml")
public class TestThreadPool {

    Logger logger = LoggerFactory.getLogger(TestThreadPool.class);

    final static CountDownLatch countDownLatch = new CountDownLatch(14);

    private ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 6, 3, TimeUnit.SECONDS,
            new ArrayBlockingQueue<Runnable>(5));
    private ExecutorService executor2 = Executors.newCachedThreadPool();
    private ExecutorService executor3 = Executors.newSingleThreadExecutor();
    private ExecutorService executor4 = Executors.newFixedThreadPool(3);
    private ExecutorService executor5 = Executors.newScheduledThreadPool(2);

    @Test
    public void test() throws InterruptedException {
        for(int i=0;i<14;i++){
            MyTestTask myTestTask = new MyTestTask(i);
            executor.execute(myTestTask);
            logger.info("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+
                    executor.getQueue().size()+",已执行完别的任务数目:"+executor.getCompletedTaskCount());
            if(i == 6){
                //总共14个任务,核心线程数设置5个,最大线程数6个,
                // 我们让前7个任务执行完等待10秒,是为了让设置的销毁非核心线程的时间3秒起作用。
                //后面就能看到非核心线程的创建,销毁和再次创建。
                Thread.sleep(10000);
            }
        }
        executor.shutdown();

        countDownLatch.await();
    }
    class MyTestTask implements Runnable {
        private int taskNum;

        public MyTestTask(int num) {
            this.taskNum = num;
        }

        @Override
        public void run() {
            try {
                logger.info("正在执行的taskNum: "+taskNum);
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                logger.info("task: "+taskNum+"执行完毕");
            } catch (Exception e) {
                e.printStackTrace();
            }finally{
                countDownLatch.countDown();
            }
        }
    }
}

执行完的结果是:

先创建5个线程执行5个任务01234
3秒后01234执行完
接着创建56任务
与此同时主线程执行sleep10秒
3秒后56任务执行完
当主线程醒来,再次执行后面的任务。

[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 0
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:1,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:2,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 1
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:3,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 2
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:4,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 3
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:0,已执行完别的任务数目:0
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 4
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:0
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:0

[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - task: 0执行完毕
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - task: 1执行完毕
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - task: 3执行完毕
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - task: 2执行完毕
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - task: 4执行完毕

[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 5
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 6
[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - task: 5执行完毕
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - task: 6执行完毕

[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 7
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 8
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 9
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 10
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 11
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:1,已执行完别的任务数目:7
[main] INFO  com.zs.test.TestThreadPool - 线程池中线程数目:5,队列中等待执行的任务数目:2,已执行完别的任务数目:7
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - task: 7执行完毕
[pool-1-thread-3] INFO  com.zs.test.TestThreadPool - task: 8执行完毕
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - task: 9执行完毕
[pool-1-thread-2] INFO  com.zs.test.TestThreadPool - task: 10执行完毕
[pool-1-thread-1] INFO  com.zs.test.TestThreadPool - task: 11执行完毕
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 12
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - 正在执行的taskNum: 13
[pool-1-thread-5] INFO  com.zs.test.TestThreadPool - task: 12执行完毕
[pool-1-thread-4] INFO  com.zs.test.TestThreadPool - task: 13执行完毕
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值