【从面试题建立知识体系】JAVA面试题-----------并发编程------线程池

并发编程(二)------线程池

什么是线程池

线程池(Thread Pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,对线程统一管理。

线程池就是存放线程的池子,池子里存放了很多可以复用的线程。

创建线程和销毁线程的花销是比较大的(手动new Thread类),创建和消耗线程的时间有可能比处理业务的时间还长。这样频繁的创建线程和销毁线程是比较消耗资源的。(我们可以把创建和销毁的线程过程去掉)。

使用线程池的优势

  1. 提高效率,创建好一定数量的线程放在池中,等需要使用的时候就从池中拿一个,这要比需要的时候创建一个线程对象要快的多。
  2. 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。
  3. 提高系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间;

创建线程池的几种方式

总体来说线程池的创建可以分为以下两大类:

1.通过ThreadPoolExecuter手动创建线程池

2.通过Executors执行器自动创建线程池

在这里插入图片描述

线程池的几种状态

通过线程池源码查看可以发现由ctl属性来标识当前线程池的状态。
状态总共有五种

  • RUNING
  • SHUTDOWN
  • STOP
  • TIDYING
  • TERMINATED

通过32bit标识,高3位标识当前线程执行状态,低29位标识线程总数。

通过为位移运算可以得出,当高三位为111时,线程池为RUNING状态,000为SHUTDOWN,001为STOP,010为TIDYING状态,011为TERMINATED.

	// ctl标识了两种状态
	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	// 高三位为线程池状态
	// 低29位为线程池中工作线程总数
    private static final int COUNT_BITS = Integer.SIZE - 3;
    // 标识工作线程最大数量
    // 00011111 11111111 11111111 11111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;
	
    // 111 左位移29位
    // 11111111 11111111 11111111 11111111
    // 11100000 00000000 00000000 00000000
    private static final int RUNNING    = -1 << COUNT_BITS;
    // 000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    // 001
    private static final int STOP       =  1 << COUNT_BITS;
    // 010
    private static final int TIDYING    =  2 << COUNT_BITS;
    // 011
    private static final int TERMINATED =  3 << COUNT_BITS;

    // 计算出当前线程池的状态
    // 00000000 00000000 00000000 00000000
    // 00011111 11111111 11111111 11111111
    // &运算后值
    // 11100000 00000000 00000000 00000000
    // ~取反
    // 00011111 11111111 11111111 11111111
    private static int runStateOf(int c)     { return c & ~CAPACITY; }
    // 计算出当前线程池中工作线程的个数
    // 00000000 00000000 00000000 00000001
    // 00011111 11111111 11111111 11111111
    // &运算后值
    // 00000000 00000000 00000000 00000001
    private static int workerCountOf(int c)  { return c & CAPACITY; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }

状态详解

状态名称描述
RUNING运行中状态,可以接受新的任务和执行队列中的任务
SHUTDOWN需要手动调用shutdown()方法关闭状态,不在接受新的任务,但是会把任务队列中的任务执行完,调用shutdownNow方法流转至STOP状态
STOP停止状态,不在接受新的任务,也不会在执行任务队列中的任务,并且会把正在工作中的线程中断销毁。手动调用shutdownNow()方法流转至该状态 ,工作线程销毁完后流转至TIDIYING中间状态
TIDYING整理中状态,所有任务都已经执行完毕,工作线程数为0,过渡到此状态的工作线程会调用钩子方法tedminated()
TERMINATE终止状态,钩子方法terminated()执行完毕

execute方法源码

public void execute(Runnable command) {
		//执行非空判断
        if (command == null)
            throw new NullPointerException();
        //获取ctl属性
        int c = ctl.get();
        //判断当前工作线程个数是否小于核心线程数大小
        if (workerCountOf(c) < corePoolSize) {
        	// 如果小于的话添加核心线程执行command任务
            if (addWorker(command, true))
            	//添加成功直接返回
                return;
            // 添加失败
            // 重新获取ctl属性,此处可能存在并发情况导致ctl属性变更
            c = ctl.get();
        }
        // 添加核心线程失败的情况
        // 判断当前线程池是否是RUNNING状态,并且将任务添加到工作队列中等待执行
        // 添加核心线程失败有可能是线程池状态发生变化导致,因此需要判断当前线程池状态
        if (isRunning(c) && workQueue.offer(command)) {
        	// 添加任务到工作队列成功
        	// 此处无法保证原子性,可能存在并发安全问题
        	// 再次获取ctl值
            int recheck = ctl.get();
            // 可能发生当前线程添加任务成功后,其他线程修改了线程池状态
            // 判断当前线程池是否非RUNNING状态,如果是非RUNNING状态,那么当前线程池处于在被关闭的过程中,需要移除当前添加的任务
            if (! isRunning(recheck) && remove(command))
            	// 如果是非RUNNING状态,且删除当前任务成功,执行拒绝策略
                reject(command);
            // 当前线程池状态为RUNNING状态,或者删除任务失败
            // 判断当前工作线程是否为0
            else if (workerCountOf(recheck) == 0)
            	// 工作线程数为0,但此时工作队列中有任务在排队
            	// 创建一个空任务非核心线程来处理工作队列中的任务
                addWorker(null, false);
        }
        // 任务添加到工作队列失败,当前工作队列已满,尝试创建非核心线程执行command任务
        // addWorker创建成功返回ture,此处取反为创建失败的情况
        else if (!addWorker(command, false))
        	// 创建非核心线程失败,执行拒绝策略
            reject(command);
    }

addWorker方法源码

    private boolean addWorker(Runnable firstTask, boolean core) {
    	//对线程池状态的判断,以及对工作线程数量的判断

		// retry: 外层for循环的标识
        retry:
        for (;;) {
        	// 获取ctl的值
            int c = ctl.get();
            // 获取当前线程池的状态
            int rs = runStateOf(c);

            // 只有不为RUNNING状态的值才会>=SHUTODWN
            // 那么只要是RUNNING状态该if判断则为false
            if (rs >= SHUTDOWN &&
            	// 到这里说明当前线程是非RUNNING状态,判断当前提交的任务是否可以不处理
            	// 如果当前状态为SHUTDOWN并且提交的任务为空,工作队列不为空,说明本次提交的任务是需要处理的
            	// rs == SHUTDOWN 为false,说明当前线程池为STOP或者后续状态,则直接返回false不处理本次提交的任务
                ! (rs == SHUTDOWN &&
                // 当前线程池为SHUTDOWN状态,并且提交任务为空,工作队列不为空
                // 说明当前执行的是addWorker(null,false)方法,需要处理工作队列的任务
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                // 满足return false的情况
                // 当前线程池状态为非RUNNING状态,并且状态不为SHUTDOWN,说明当前线程池状态可能为STOP,TIDYING,TERMINATE
                // 当前线程池状态为非RUNNING状态,状态为SHUTDOWN,提交的任务不为null
                // 当前线程池状态为非RUNNING状态,状态为SHUTDOWN,提交的任务为null,但是工作队列为空
                
                return false;
			// 到这里说明当前线程状态为RUNNING或者SHOTDOWN
			// 当前任务任务可能不为null或者不为null
			// 工作队列中可能存在或者不存在任务
            for (;;) {
            	// 基于获取到的ctl获取当前工作线程数量
                int wc = workerCountOf(c);
                // 判断工作线程是否大于最大值
                if (wc >= CAPACITY ||
                	// 如果需要创建的是核心线程,是否大于设置的corePoolSize
                	// 如果是非核心线程,是否大于设置的maximumPoolSize
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    // 大于直接返回创建失败
                    return false;
                // 基于CAS操作对工作线程数+1
                if (compareAndIncrementWorkerCount(c))
                	// 如果成功跳出外层for循环
                    break retry;
                // 如果失败了说明有并发操作,当前线程池状态和工作线程数可能发生变化
                // 重新读取ctl值
                c = ctl.get();  // Re-read ctl
                // 判断重新获取的ctl值线程状态是否和之前获取到的一致
                if (runStateOf(c) != rs)
                	// 如果不一致则跳出本次循环,重新判断状态
                    continue retry;
            }
        }

		//添加工作线程并启动

		// 工作线程启动标识
        boolean workerStarted = false;
        // 工作线程添加成功标识
        boolean workerAdded = false;
        // 工作线程对象
        Worker w = null;
        try {
        	//  new Worker()对象,并且将当前任务传入
            w = new Worker(firstTask);
            // 获取到worker对象中绑定的thread线程
            final Thread t = w.thread;
            // 如果t不为null,健壮性判断
            if (t != null) {
            	// 线程池中的全局锁
                final ReentrantLock mainLock = this.mainLock;
                // 通过AQS上锁
                mainLock.lock();
                try {
                	// 重新获取ctl用于判断状态
                	// 避免在获取到锁前当前线程池状态被修改
                    int rs = runStateOf(ctl.get());
					
					// 只有RUNNING状态会小于SHUTDOWN,如果为RUNNING则不进行后续判断直接为ture
                    if (rs < SHUTDOWN ||
                    	// 如果当前状态为SHOTDOWN并且firstTask为null,说明当前为addWorker(null,false),需要创建工作线程
                        (rs == SHUTDOWN && firstTask == null)) {
                        //预检查,健壮性判断,判断当前线程是否处于run状态
                        if (t.isAlive())
                            throw new IllegalThreadStateException();

						// 将当前创建好的worker对象扔到workers中
                        workers.add(w);
                        // 获取工作线程Set大小
                        int s = workers.size();
                        // 判断当前工作线程数是否大于历史最大工作线程数
                        if (s > largestPoolSize)
                        	// 大于则记录当前工作线程数为历史最大值
                            largestPoolSize = s;
                        // 设置工作线程添加成功标识
                        workerAdded = true;
                    }
                } finally {
                	// 锁释放
                    mainLock.unlock();
                }
                // 如果当前工作线程添加成功
                if (workerAdded) {
                	// 启动工作线程Thread
                    t.start();
                    // 启动成功标识设置
                    workerStarted = true;
                }
            }
        } finally {
        	// 如果当前工作线程启动失败
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }


// 启动工作线程失败调用该方法,传入创建的工作线程
private void addWorkerFailed(Worker w) {
		// 获取全局锁
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	// 如果当前工作线程不为null
            if (w != null)
            	// 从工作线程集合中删除该工作线程
                workers.remove(w);
            // 对工作线程数-1
            decrementWorkerCount();
            // 尝试将线程池状态修改为TIDYING
            tryTerminate();
        } finally {
            mainLock.unlock();
        }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值