java多线程线程池原理剖析

Java线程池

线程复用

线程的创建和销毁,都是重量级工作,非常的消耗性能,高并发或者频繁的线程使用场景下对机器的要求很高,会造成性能瓶颈

可以使用线程池优化线程的频繁使用场景,使线程达到重复使用而不销毁,原理类似数据库连接池,http连接池等…

线程池概念

在线程池中,当你需要使用线程时,可以从池子中随便拿一个空闲线程,当完成工作时,并不急着关闭线程,而是将这个线程退回到线程池中

相当于创建线程变成了从线程池中获取线程
关闭线程变成了将线程退回给线程池

线程池工作原理

线程池定义:

  1. 核心线程数
  2. 最大线程数
  3. 保持空闲最大时长
  4. 工作队列
  5. 拒绝策略

image-20210509233849851

  1. 线程池中原本没有线程,当任务队列中有任务的时候,才通过线程工厂创建线程,创建好的线程在线程池中为空闲状态,空闲状态的线程会以自旋的方式不断的从任务队列中取出任务执行,如果线程正在执行任务中,则为活跃状态,任务执行结束后又会回到空闲状态
  2. 线程工厂不会一直创建线程,当线程达到了线程池的最大线程数时就不会再创建线程
  3. 创建出来的线程不会一直存在于线程池中,如果线程池中的线程数量 > 核心线程数,则线程池会销毁处于空闲状态并持续了[保持空闲最大时长]的线程
  4. 任务队列中的一个任务被线程获取执行了则会从队列中移除
  5. 如果线程池中的所有线程都处于活跃中,则来不及处理的任务就会被堆积在任务队列中
  6. 当任务队列无法再存储更多任务的时候,这些无法存储的任务就会执行拒绝策略

ThreadPoolExecutor原理

源码原理

ThreadPoolExecutor构造器

	/**
     * 创建一个线程池
     *
     * @param corePoolSize 核心线程数
     * @param maximumPoolSize 最大线程数
     * @param keepAliveTime 保持空闲最大时长
     * @param unit 时间单位
     * @param workQueue 工作队列,阻塞队列
     * @param threadFactory 线程工厂
     * @param handler 拒绝执行的策略
     */
	public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler) {
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

常见的实现

image-20210509235440042

拒绝策略

image-20210509235523834

任务阻塞队列

image-20210509235558301

如何实现线程复用

如何实现Runable被执行完成后线程不会结束

答案是ThreadPoolExecutor.Worker

public class ThreadPoolExecutor extends AbstractExecutorService {

    // ...忽略其他代码
    
    private final class Worker extends AbstractQueuedSynchronizer implements Runnable {
        
        // ...忽略其他代码
        
        /** Thread this worker is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            this.thread = getThreadFactory().newThread(this);
        }
        
        public void run() {
            runWorker(this);
        }
        
        final void runWorker(Worker w) {
            Thread wt = Thread.currentThread();
            Runnable task = w.firstTask;
            w.firstTask = null;
            w.unlock(); // allow interrupts
            boolean completedAbruptly = true;
            try {
                while (task != null || (task = getTask()) != null) {
                    w.lock();
                    // If pool is stopping, ensure thread is interrupted;
                    // if not, ensure thread is not interrupted.  This
                    // requires a recheck in second case to deal with
                    // shutdownNow race while clearing interrupt
                    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();
                    }
                }
                completedAbruptly = false;
            } finally {
                processWorkerExit(w, completedAbruptly);
            }
        }
        
    }
    
}

可以看到,实际线程池在创建线程的时候,线程中执行的Runnable并不是我们的Runnable任务
线程池自己实现了一个Runnable,用于绑定一个线程,并持续的在线程中自旋持续不断的从任务队列中获取出我们的Runnable并执行一次,这就是为什么线程在执行完我们的Runnable任务后,线程仍然存在并且没有关闭的原因,因为线程执行的实际上是WorkerWorker一直没有停止工作,是Woeker在执行我们的Runnable任务

newCachedThreadPool

    public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>(),
                                      threadFactory);
    }

缓存线程池newCachedThreadPool实际上并没有核心线程数,所以其实是将所有的线程都缓存60秒,就类似给Redis的key设置过期时间为60s一样,在线程空闲60秒后就会被销毁

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值