并发编程-java线程池走读(jdk的juc包中自有线程池之ThreadPoolExecutor)

背景

juc是jdk的并发包,最核心的有三个类,本系列博客将逐一进行阐述,最后配上实战。

线程池源码走读

结构总览

诞生于jdk1.5,下面看一下类图。类的实现以两个“执行器”接口类为导向。
在这里插入图片描述
这里只看一下ThreadPoolExecutor的实现:
在这里插入图片描述

核心实现

构造过程

在这里插入图片描述
参数如下:

  • corePoolSize,核心线程数
  • maximumPoolSize,最大线程数
  • keepAliveTime,非核心线程存活时间
  • workQueue,任务对列
  • threadFactory,线程工厂类(生产线程的地方,可以指定线程名称等参数)
  • RejectedExecutionHandler,线程池和任务对列满了以后用到

执行方法submit过程

看一下submit(此方法在抽象父类里)执行的时序图(时序图在面向过程编程中非常重要,是必看的)
在这里插入图片描述

    /**
     * @throws RejectedExecutionException {@inheritDoc}
     * @throws NullPointerException       {@inheritDoc}
     */
    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        // 做了一个Future(可以通过future进行线程执行体的控制),然后调用多态的execute方法,让子类(这里的子类实现类就是ThreadPoolExecutor)去实现。
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

我们再来看execute。使用时序图去查看,出现了很深的调用,而且从类上来看,大部分是线程同步和锁,这些全部是为了线程的正常执行而服务。
在这里插入图片描述

		// 线程池的数据全部浓缩到了一个原子的int上,即ctl
        int c = ctl.get();
        // 核心线程数够的情况
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true))
                return;
            c = ctl.get();
        }
        // 尝试在对列中添加任务,资源不够就抛出
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        else if (!addWorker(command, false))
            reject(command);

execute过程大致可分为三个过程:

  • 如果执行线程数小于核心线程数,直接添加核心线程数
  • 如果执行线程数小于核心线程数,而且还有资源去添加更多线程的话,就去添加大于核心线程数的线程
  • 如果资源不够,进行“拒绝的回调”,把数据返回给调用层

线程池关闭过程 shutdown

又是一个时序图较为复杂的调用,以下仅供参考。。。
在这里插入图片描述
直接来看代码:

    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
        	// 检查权限
            checkShutdownAccess();
            // 指定目标状态
            advanceRunState(SHUTDOWN);
            // 底层调用interrupt方法去终止空闲的线程
            interruptIdleWorkers();
            // 没啥用。。
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        // 把线程池改为终止状态
        tryTerminate();
    }

简述shutdown的过程:

  • 改变状态,不再接受新的任务
  • 每个空闲线程调用interrupt,强制终止线程(这里不调用stop,因为Thread的stop方法存在bug,jdk里面有说明)
  • 等待所有任务执行完毕,终止线程池

其他接口

shutdownNow

    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            // 直接转变为STOP状态
            advanceRunState(STOP);
            interruptWorkers();
            // 返回没有执行过的task
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

与shutdown不同的是,这个方法不再等待所有的任务执行完毕,而是把没有完成的任务列表返回。

awaitTermination

实战

shutdown

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
        );
        executor.submit(() -> {
            try {
                Thread.sleep(10000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("调用了shutdown");
        executor.shutdown();
        System.out.println("执行完毕");
    }

shutdown执行以后,需要等执行的线程自然结束以后,才会到最后的打印。

shutdownNow

    public static void main(String[] args) {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
        );
        executor.submit(() -> {
            try {
                Thread.sleep(10000000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println("调用了shutdown");
        executor.shutdownNow();
        System.out.println("执行完毕");
    }

执行shutdownNow后,由于直接对忙碌线程调用了interrupt方法,所以抛出以下异常,并且线程直接被打断
在这里插入图片描述

注:shutdownNow方法无法结束循环操作,实例如下

public class Main {
    private static long i;

    public static void main(String[] args) throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        executorService.submit(new Runnable() {
            @Override
            public void run() {
                while (true) {
                    System.out.println(i++);
                }
            }
        });
        Thread.sleep(1000);
        executorService.shutdownNow();
    }
}

一直在进行++的打印
在这里插入图片描述

awaitTermination

    public static void main(String[] args) throws InterruptedException {
        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                10, 20, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>()
        );
        executor.submit(() -> {
            try {
                Thread.sleep(2000);
                System.out.println(String.format("%s, 执行完毕", Thread.currentThread().getName()));
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        System.out.println(executor.awaitTermination(3, TimeUnit.SECONDS));
        System.out.println("查看shutdown状态:" + executor.isShutdown());
        System.out.println("查看terminating状态:" + executor.isTerminating());
        System.out.println("查看terminated状态:" + executor.isTerminated());
    }

执行完毕后,出现以下打印,并且发现程序没有终止,线程池还在活跃。从现象可以证明await的过程只是等待而已,并不去操作线程池本身。
在这里插入图片描述

prestartAllCoreThreads,prestartCoreThread

提前启动核心线程,这样就免除了在使用时启动的时间。就不举例子了。

评价

线程池底层使用到了众多线程同步处理操作:

  • Condition,用于同步
  • Queue,用于传递数据
  • Future,FutureTask等,用于获取返回结果和task控制
    未完待续。。。。。。。。
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值