并发编程之线程池原理(一)

自定义一个线程池

 public class CustomThreadPoolExample {

	// 任务类
    static class CThread implements Runnable{

        private int k ;
        public CThread(int i){
            k = i;
        }


        @Override
        public void run() {
            System.out.println("执行中 " + Thread.currentThread().getName()  + "  " +  k );
            try {
                int a = 1 ; int b = 0 ;
                int c = a / b ;
                Thread.sleep(100);
            } catch (InterruptedException e) {
//                e.printStackTrace();
            }
        }
    }

	// 测试
    public static void main(String[] args) {
        LinkedBlockingQueue lbq = new LinkedBlockingQueue(3);
        ExecutorService es = new ThreadPoolExecutor(5,5,1, TimeUnit.SECONDS,lbq){

            @Override
            protected void beforeExecute(Thread t, Runnable r) {
                System.out.println("执行之前干点啥");
            }



            @Override
            protected void afterExecute(Runnable r, Throwable t) {
                System.out.println("执行之后干点啥");
            }


            @Override
            protected void terminated() {
                System.out.println("线程将被销毁");
            }
        };

        for (int i = 0 ; i < 1 ; i++){
            try {
                es.execute(new CThread(i));
            } catch (Exception e) {
//                e.printStackTrace();
            }
        }

        System.out.println("--------------结束------------------");
        // 防止线程池没有完成,主线程却退出了
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            //e.printStackTrace();
        }

        es.shutdown();


    }


}

主要看一下,线程池的几个参数

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue) {
        this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
             // 使用的线程创建工厂类为java.util.concurrent.Executors.DefaultThreadFactory
             // 拒绝策略为RejectedExecutionHandler defaultHandler = new AbortPolicy();
             // 也就是抛出异常
             Executors.defaultThreadFactory(), defaultHandler);
    }

execute进入看到java.util.concurrent.ThreadPoolExecutor#execute实现

ctl是什么

    
	private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
	//数量位数 29 
    private static final int COUNT_BITS = Integer.SIZE - 3;
    //有效线程数量为   2^29-1  = 536870911
    //也就是00011111111111111111111111111111
    private static final int CAPACITY   = (1 << COUNT_BITS) - 1;

    //线程池状态值
    //11100000000000000000000000000000
    private static final int RUNNING    = -1 << COUNT_BITS;
    //00000000000000000000000000000000
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    //100000000000000000000000000000
    private static final int STOP       =  1 << COUNT_BITS;
    //1000000000000000000000000000000
    private static final int TIDYING    =  2 << COUNT_BITS;
    //1100000000000000000000000000000
    private static final int TERMINATED =  3 << COUNT_BITS;

ctl是一个控制状态机,是一个原子整数,其中包含两部分
runState 前三位 表示线程池状态

状态解释
RUNNING初始状态
SHUTDOWN调用shutdown方法,表示停止中,会对所有有效线程进行一次中断
STOP
TIDYING当状态为SHUTDOWN 并且 队列中没有任务之后 状态流转为 TIDYING
TERMINATED状态修改为TIDYING 成功之后,调用一次扩展方法terminated,然后将状态修改为TERMINATED

workerCount 后29位表示线程的有效数量,最大值为 2^29-1 = 536870911 ,也就是后29位全1

有了ctl的基础知识再来看源码

  public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        //初始值为11100000000000000000000000000000 也就是 RUNNING    
        int c = ctl.get();
        // 取有效线程数量 与 核心线程数 比较
        // 如何取数量 & 操作
        // 00011111111111111111111111111111
        // &
        // 11100000000000000000000000000000
        // 0
        // corePoolSize 是多少,看前面的构造方法 corePoolSize = 0
        // 如果工作线程(运行中)没有超过核心线程数
        if (workerCountOf(c) < corePoolSize) {
            // 添加核心线程并将当前任务作为该线程作为第一个任务执行
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 如果工作线程数超过了核心线程数,将任务添加到任务队列
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            // 这里又判断一次线程池状态,类似double-check,如果不是运行中了,删除任务,执行拒绝策略
            if (! isRunning(recheck) && remove(command))
                reject(command);
            // 如果有效线程数量=0,意味着corePoolSize=0,创建扩容线程
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);
    }

所以核心线程,任务队列,扩展线程,拒绝策略这四个东西的优先级是怎样的呢?

  • 有效线程数量小于核心线程数,创建核心线程,并将当前任务作为该线程的第一个任务执行
  • 有效线程数大于等于核心线程数,当前任务添加到队列
    1. 再次校验状态,如果不是运行中了,删除任务,并执行拒绝策略
    2. 再次校验数量,如果线程数量为0,增加扩展线程,并且第一个任务传null
      为什么不用传第一个任务,因为已经添加到队列了,后续会从队列拿出来执行
      为什么是新增扩展线程,因为能走到这里,并且有效线程数量=0那意味着corePoolSize=0
  • 增加扩展线程,当前任务作为第一个新增线程的第一个任务执行,如果增加扩展线程失败,执行拒绝策略
   // 参数一:当前要执行的命令
   // 参数二: 是否为核心线程
   private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            int c = ctl.get();
            // 取线程池状态
            int rs = runStateOf(c);

            // 仅在必要时检查队列是否为空。
            // 什么情况下返回false ,这里要针对具体情境
            // 线程池状态  将停止或者已经停止 && !(将停止 && 当前任务为null && 队列不为空) 
            if (rs >= SHUTDOWN &&
                ! (rs == SHUTDOWN &&
                   firstTask == null &&
                   ! workQueue.isEmpty()))
                return false;

            for (;;) {
            	// 有效线程数量
                int wc = workerCountOf(c);
                // 大于容量,大于最大线程数的场景可以不考虑,当前core=0
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))
                    return false;
                // 原子操作c +1,如果成功,有效线程计数成功了 跳出两层循环
                // 否则继续循环,这里又有两种情况
                // 1.最新线程池状态没有发生改变,继续内层循环
                // 2.最新线程池状态发生改变,继续外层循环
                if (compareAndIncrementWorkerCount(c))
                    break retry;
                c = ctl.get();  // Re-read ctl
                if (runStateOf(c) != rs)
                    continue retry;
                // else CAS failed due to workerCount change; retry inner loop
            }
        }

		// 上面的步骤保证了两个问题,1.线程池状态正常,2.有效线程数+1

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
        	// 实例化worker,逻辑上它就是线程池里面的线程
            w = new Worker(firstTask);
            final Thread t = w.thread;
            if (t != null) {
            	// 加锁
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    int rs = runStateOf(ctl.get());

                    if (rs < SHUTDOWN ||
                        (rs == SHUTDOWN && firstTask == null)) {
                        if (t.isAlive()) // 校验线程是不是已经启动,这里预期是新的线程
                            throw new IllegalThreadStateException();
                        // workers是个HashSet,也就是说,线程实际是保存在这个集合的
                        workers.add(w);
                        int s = workers.size();
                        // largestPoolSize 记录池子里面的最大线程池数量,也就是峰值,由于只在lock下访问,它是线程安全的
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        // 标记添加线程到池成功
                        workerAdded = true;
                    }
                } finally {
                    mainLock.unlock();
                }
                // 启动线程
                if (workerAdded) {
                    t.start();
                    // 标记线程启动成功
                    workerStarted = true;
                }
            }
        } finally {
        	// 线程启动失败的处理,其实就意味这线程池不可用,需要从池里删除,并计数-1 ,然后执行tryTerminate方法
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
        Worker(Runnable firstTask) {
        	// 设置为-1 表示禁止中断
            setState(-1); 
            this.firstTask = firstTask;
            // 通过线程工厂创建线程,线程工厂开发者不提供就是使用的默认 DefaultThreadFactory
            this.thread = getThreadFactory().newThread(this);
        }
        
        public Thread newThread(Runnable r) {
            Thread t = new Thread(group, r,
                                  namePrefix + threadNumber.getAndIncrement(),
                                  0);
            // 固定为非守护线程,固定线程优先级为默认优先级
            if (t.isDaemon())
                t.setDaemon(false);
            if (t.getPriority() != Thread.NORM_PRIORITY)
                t.setPriority(Thread.NORM_PRIORITY);
            return t;
        }

	final void tryTerminate() {
        for (;;) {
            int c = ctl.get();
            // 运行中 || 终止中  || (停止中&&队列不为空) 这三种情况不需要继续处理了
            // 要么还在用,要么已经触发过关闭了,正在处理
            if (isRunning(c) ||
                runStateAtLeast(c, TIDYING) ||  
                (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty()))
                return;
            // 给所有线程中断信号
            if (workerCountOf(c) != 0) { // Eligible to terminate
                interruptIdleWorkers(ONLY_ONE);
                return;
            }
			// 到这里要满足什么条件 
			// 一种是 SHUTDOWN && workQueue.isEmpty()
			// 一种是状态为 STOP
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
            	// cas改状态为 TIDYING
                if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) {
                    try {
                    	// 这是个空方法,提供开发者扩展的
                        terminated();
                    } finally {
                    	// 状态改成 TERMINATED 终止状态
                        ctl.set(ctlOf(TERMINATED, 0));
                        // 发出终止信号,也是用于扩展
                        termination.signalAll();
                    }
                    return;
                }
            } finally {
                mainLock.unlock();
            }
            // else retry on failed CAS
        }
    }

Worker类,如何执行任务

首先贴上Worker的类图
worker类
主要看看run方法,直接调用了runWorker(this)方法,这里还得先描述下线程中断
中断主要有三个方法:

  • Thread.currentThread().interrupt();
    1. 修改中断的标记,false->true
    2. 唤醒处于阻塞状态下的线程
    3. 抛出 InterruptedException异常同时中断状态复位 true->false
  • Thread.currentThread().isInterrupted(); 获取中断状态
  • Thread.interrupted(); 返回线程中断标记 然后进行中断状态复位 true->false
 final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        Runnable task = w.firstTask;
      	// 第一个任务,只使用一次
        w.firstTask = null;
        // 允许中断了,为什么进来就允许中断了
        w.unlock();
        // 标记线程如何结束的
        boolean completedAbruptly = true;
        try {
        	// 典型的线程玩法,死循环执行任务
        	// 如果构造时传入任务,作为第一个执行,后续从队列中获取执行
            while (task != null || (task = getTask()) != null) {
                w.lock();
                // 如果池正在停止,请确保线程被中断;如果没有,请确保线程不被中断。在第二种情况下需要重新检查,以处理关闭在清除中断时无竞争
				//  (当前状态 >= STOP ||  (被中断 && 当前状态 >= STOP)) &&  中断标志已经复位
				//   这是典型的中断用法,外部进行中断,线程内部得到信号决定要不要结束线程
                if ( (runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) 
                &&  !wt.isInterrupted())
                    wt.interrupt();
                try {
                	// 执行前可以插入逻辑,用于扩展
                    beforeExecute(wt, task);
                    Throwable thrown = null;
                    try {
                    	// 执行任务
                        task.run();
                    } catch (RuntimeException x) {
                        thrown = x; throw x;
                    } catch (Error x) {
                        thrown = x; throw x;
                    } catch (Throwable x) {
                        thrown = x; throw new Error(x);
                    } finally {
                    	// 执行任务后可以插入逻辑,用于扩展
                        afterExecute(task, thrown);
                    }
                } finally {
                    task = null;
                    // 记录线程完成任务数量
                    w.completedTasks++;
                    w.unlock();
                }
            }
            // 如果为false意味着线程正常结束的,true表示异常终止,比如异常
            completedAbruptly = false;
        } finally {
        	// 线程退出的处理
            processWorkerExit(w, completedAbruptly);
        }
    }

从队列中获取任务

   private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?

        for (;;) {
            int c = ctl.get();
            int rs = runStateOf(c);

            // Check if queue empty only if necessary.
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }

            int wc = workerCountOf(c);

            // 如果不考虑程序员干预 allowCoreThreadTimeOut  的话
            // 是否使用超时阻塞获取任务完全由 wc > corePoolSize 决定
            // 这里也就表明,核心线程数和扩容线程数没有任何区别,只是当有扩容线程的时候,允许超过核心线程数的线程退出
            // 也就是说池子要保证有效线程数量不低于corePoolSize
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;

            if ((wc > maximumPoolSize || (timed && timedOut))
                && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }

            try {
                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }
private void processWorkerExit(Worker w, boolean completedAbruptly) {
		// 异常退出的,要处理有效线程计数
        if (completedAbruptly) 
            decrementWorkerCount();

        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            // 已完成任务的总计数器。仅在工作线程终止时更新。仅在mainLock下访问。
            completedTaskCount += w.completedTasks;
            workers.remove(w);
        } finally {
            mainLock.unlock();
        }
		// 执行尝试终止
        tryTerminate();

        int c = ctl.get();
        // 池子状态 为 RUNNING   SHUTDOWN   这两种状态的时候
        if (runStateLessThan(c, STOP)) {
        	// 线程正常执行结束
            if (!completedAbruptly) {
            	//如果为false(默认值),核心线程即使在空闲时也会保持活动状态。如果为true,核心线程将使用keepAliveTime来超时等待工作。
            	// allowCoreThreadTimeOut  其实是一个辅助开关,可以手动设置
            	// 设置了allowCoreThreadTimeOut 或者工作线程数 >  核心线程数  使用poll(keepAliveTime) 获取任务 否则使用take 获取任务
            	// 这里简单来说,就是校验最小的有效线程数,如果传了核心线程,那有效线程数>=核心线程数
            	// 如果核心线程数=0 那在队列不为空的情况下,最少要有一个有效线程在运行
            	// 否则,补充一个扩容线程放入线程池
                int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
                if (min == 0 && ! workQueue.isEmpty())
                    min = 1;
                if (workerCountOf(c) >= min)
                    return; // replacement not needed
            }
            addWorker(null, false);
        }
    }
  • 9
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

懒眉

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值