总结 : 线程池

什么线程池

线程池是用来处理线程并发的一个功能;他主要解决线程任务过多时,之前无休止的创建和销毁线程,造成线程数目不可控和系统卡顿等性能问题。线程池能控制线程的数目,同时复用空闲线程,执行未处理的线程任务;控制了最大线程数目,减少的线程创建销毁的开销;

线程池体系在这里插入图片描述

java主要使用ThreadPoolExecutor,

线程池参数配置

    public static void main(String[] args) {
        //核心线程数目
        int corePollSize = 3;
        //最大线程数
        int maximumPoolSize = 6;
        //额外线程的保存时间
        long keepAliveTime = 60L;
        TimeUnit unit = TimeUnit.MILLISECONDS;
        //阻塞队列
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>();
        //线程工厂
        ThreadFactory threadFactory = Executors.defaultThreadFactory();
        //拒绝策略
        RejectedExecutionHandler handler = new ThreadPoolExecutor.AbortPolicy();
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                corePollSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue,
                threadFactory,
                handler);
        threadPoolExecutor.submit(new Runnable() {
            @Override
            public void run() {
                System.out.println("task one");
            }
        });
    }

配置线程池的几个重要参数

  • corePollSize:表示核心线程数量
  • maximumPoolSize:表示最大线程池数目,在阻塞队列满了之后,最大还可以创建(maximumPoolSize - corePollSize)个额外的线程
  • keepAliveTime:额外线程在执行完任务后保留的时间
  • unite:时间单元,与keepAliveTime关联使用
  • workQueue:阻塞队列
  • threadFactory :线程创建的工厂类
  • handler :拒绝策略,如果新的任务无法加入到阻塞队列,且无法再创建额外线程了,就执行拒绝策略

线程池源码分析

开始一个新的线程任务,可以通过execute,也可以submit,建议使用submit,它会把Runable包装到RunnableFuture中,然后再执行execute;

    public Future<?> submit(Runnable task) {
        if (task == null) throw new NullPointerException();
        RunnableFuture<Void> ftask = newTaskFor(task, null);
        execute(ftask);
        return ftask;
    }

RunnableFuture相对了Runable多实现了取消任务的接口
在这里插入图片描述
开始execute
在这里插入图片描述
这里的ctl时一个AtomicInteger对象,一个原子操作的 int值,他存在多个状态信息,后29为用来记录配置的最大核心线程数;通过workerCountOf获取当前线程池创建的多少核心线程数;

  1. 如果当前核心线程数没有操作配置的最大核心线程数,那么就会通过addWorker创建新的核心线程执行线程任务,然后return
  2. 如果不满足上步,说明当前核心线程已经都跑起来了,通过isRunning判断线程池是否没有停止,没有停止,把线程任务添加到阻塞队列里面。这种情况就是有超过核心线程数的任务线程在跑,但是阻塞队列还没满
  3. 当阻塞队列已经满了之后,workQueue满了之后,就会通过addWorker创建非核心线程,addWorker(command,fasle),注意有两个参数,第一个表示当前的线程任务,第二个表示是否是核心线程

addWorker是如何创建线程的

    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) {
            ......
            for (;;) {
                int wc = workerCountOf(c);
                if (wc >= CAPACITY ||
                    wc >= (core ? corePoolSize : maximumPoolSize))//(1)
                    return false;
            ......
            }
        }

        boolean workerStarted = false;
        boolean workerAdded = false;
        Worker w = null;
        try {
            w = new Worker(firstTask);//(2)
            final Thread t = w.thread;
            if (t != null) {
                final ReentrantLock mainLock = this.mainLock;
                mainLock.lock();
                try {
                    ......
                        workers.add(w);//(3)
                        int s = workers.size();
                        if (s > largestPoolSize)
                            largestPoolSize = s;
                        workerAdded = true;
                   ......
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    t.start();//(4)
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }

addWorker做了这么几件事

  1. 判断当前是否还需要创建线程,如果addWorker第二个参数是true,那么判断核心线程是否已经到达最大了,如果是false,查看最大线程是否是最大
  2. 实例化Worker,每个Worke创建都会new一个Thread,然后start启动它
  3. 把worker条件到列表里面

Worker实例化
在Works的线程执行run之后,会直接走到runWorker这个核心代码这里
在这里插入图片描述
第二个红点处就是获取搭配task直接运行
第一个红点“while (task != null || (task = getTask()) != null) ”,来从阻塞队列里面获取线程任务,最后会通过poll和take方法

                Runnable r = timed ?
                    workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                    workQueue.take();

有两个作用

  1. 核心线程不会结束:在没有新的任务的时候会一直阻塞在这里,等待新的任务添加到阻塞队列
  2. 线程任务复用线程:在执行完一个线程任务之后,线程不会结束,而是继续从阻塞队列中取任务,

非核心线程可以设置时间keepAliveTime,让非核心线程执行任务之后不会立刻死掉,而是在keepAliveTime时间后没有任务才会死掉
核心线程设置allowCoreThreadTimeOut(同时必须设置keepAliveTime),让核心线程在执行完之后,如果没任务在keepAliveTime时间后死掉;这个时候核心线程和非核心线程停止的条件一致

考点

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值