线程池源码&&日常运用

82 阅读4分钟

线程池状态

ThreadPoolExecutor 使用int的高3位标识线程池状态,低29位标识线程池数量。

image.png

线程池的使用

线程池完整构造方法

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.corePoolSize = corePoolSize;
    this.maximumPoolSize = maximumPoolSize;
    this.workQueue = workQueue;
    this.keepAliveTime = unit.toNanos(keepAliveTime);
    this.threadFactory = threadFactory;
    this.handler = handler;
}

参数释义

  • corePoolSize 核心线程数
  • maximumPoolSize 最大线程数
    • 最大线程数 = 核心线程数 + 空闲线程数
  • keepAliveTime 空闲线程存活时间
  • TimeUnit 空闲线程存活时间单位
  • BlockingQueue 任务存放队列
  • ThreadFactory 线程工厂,用于生产线程,自定义线程名称
  • RejectedExecutionHandler 拒绝策略

线程池工作原理

  • 线程池是具备懒惰性的,线程池被实例化时并不会启动任何线程。直到线程池在提交任务时创建线程;先创建核心线程,如果任务超出了核心线程则放入BlockingQueue队列中。如果队列中的任务达到上限才会创建空闲线程。
  • 在没有设置配置情况下核心线程不会被销毁
  • 核心线程并非固定的某些线程,线程池只保持核心线程数,不保证某一个线程。

线程池 execute 源码

public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    int c = ctl.get();
    //判断工作线程数是否小于核心线程数
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))//调用addWorker
            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);
}

addWorker 方法

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    for (;;) {
       ...... 
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        w = new Worker(firstTask); //封装一个worker,将任务交给worker执行
        // woker对象中的线程 t
        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.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                t.start(); //启动worker对象中的线程。启动后会执行worker对象中的run方法
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

Worker构造方法

Worker(Runnable firstTask) {
    setState(-1); 
    // 当前任务
    this.firstTask = firstTask; 
    //获取线程工厂创建一个线程
    this.thread = getThreadFactory().newThread(this);
}

Worker对象中的线程strat后执行 Worker对象中 run 方法

public void run() {
    runWorker(this);
}
final void runWorker(Worker w) {
    Thread wt = Thread.currentThread();
    //拿到当前线程任务
    Runnable task = w.firstTask;
    w.firstTask = null;
    w.unlock(); 
    boolean completedAbruptly = true;
    try {
        //当前task不为空        或者      从队列中获取task不为空
        while (task != null || (task = getTask()) != null) {
            w.lock();
            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方法
                    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);
    }
}

队列中获取task

private Runnable getTask() {
    boolean timedOut = false; 

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

        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
            decrementWorkerCount();
            return null;
        }

        int wc = workerCountOf(c);

        // allowCoreThreadTimeOut默认值为false 不允许核心线程超时被销毁
        // wc > 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 ?
                //核心线程允许被销毁或者工作线程大于核心线程 调用poll方法 
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                workQueue.take();//核心线程不允许被销毁调用take方法
            if (r != null)
                return r;
            timedOut = true;
        } catch (InterruptedException retry) {
            timedOut = false;
        }
    }
}

poll方法 核心线程的等待时间和空闲线程等待时间一致

public E poll(long timeout, TimeUnit unit) throws InterruptedException {
    long nanos = unit.toNanos(timeout);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        //count 队列中的任务数
        while (count == 0) {
            if (nanos <= 0)
                return null;
                //这里的notEmpty是一个Condition条件队列
                //如果队列中没有任务  调用awaitNanos等待设置的空闲线程等待时间后释放资源
            nanos = notEmpty.awaitNanos(nanos);
        }
        return dequeue();
    } finally {
        lock.unlock();
    }
}

核心线程数如果不允许被销毁时调用

public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            //永久阻塞
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}

模拟线程池案例

自定义线程池

@Slf4j(topic = "marx")
public class CustomerThreadPool {

    static AtomicInteger atomicInteger = new AtomicInteger(1);

    TaskQueue taskQueue;

    static boolean allowCoreThreadTimeout = false;

    public CustomerThreadPool(int coreSize, int queueSize) {
        this.coreSize = coreSize;
        this.queueSize = queueSize;
        this.taskQueue = new TaskQueue(queueSize);
    }

    //所有线程的集合
    HashSet<Worker> threadHashSet = new HashSet<>();

    //支持的最大核心线程数
    int coreSize;
    //队列线程数
    int queueSize;

    public void info(){
        log.debug("workThreadSize={}",threadHashSet.size());
    }

    //向线程池提交一个任务
    public void execute(Task task){
        //工作中线程数没有达到阈值
        if(threadHashSet.size() < coreSize){
            log.debug("线程池未满,创建线程分配任务,任务的名称--[{}]",task.getTaskName());
            //封装任务worker
            Worker worker = new Worker(task,taskQueue);
            //任务入队
            threadHashSet.add(worker);
            //启动任务
            worker.start();
        }else{
            log.debug("线程池已满,放到队列中进行排队,任务的名称--[{}]",task.getTaskName());
            taskQueue.offer(task);
        }
    }
}

线程封装的worker对象

@Slf4j(topic = "marx")
public class Worker implements Runnable{

    TaskQueue taskQueue;

    //首个任务  实例化worker对象时分配的task
    Runnable firstTask;

    Thread thread;

    public Worker(Runnable firstTask,TaskQueue taskQueue){
        this.firstTask = firstTask;
        this.taskQueue = taskQueue;
        this.thread = new Thread(this,"thread-"+CustomerThreadPool.atomicInteger.incrementAndGet());
    }

    public Thread getThread() {
        return thread;
    }

    @Override
    public void run() {
        //执行提交的任务
        while (firstTask!=null || (firstTask = taskQueue.take())!=null){
            firstTask.run();
            firstTask = null;
        }
        log.debug("没有拿到任务");
    }

    public Task getTask() throws InterruptedException {
        boolean allowCoreThreadTimeout = CustomerThreadPool.allowCoreThreadTimeout;
        return allowCoreThreadTimeout?taskQueue.poll(TimeUnit.SECONDS.toNanos(3)):taskQueue.take();
    }

    public void start(){
        log.debug("线程启动---【{}】",thread.getName());
        thread.start();
    }
}

自定义任务队列

@Slf4j(topic = "marx")
public class TaskQueue {
    //这里用reentrantLock是由于实现简单,可以在生产线程和消费线程中精确唤醒,sync则可能会将2者都唤醒
    ReentrantLock lock = new ReentrantLock();
    //生产者线程   当队列满的时候
    Condition full = lock.newCondition();
    //消费者线程   当队列为空的时候
    Condition empty = lock.newCondition();
    Deque<Task> deque = new ArrayDeque<>();
    //队列元素上限
    private int queueSize;

    public TaskQueue(int queueSize) {
        this.queueSize = queueSize;
    }

    /**
     * 向任务队列中存放当前任务 需要考虑线程安全问题,因而引入锁
     * @param task
     */
    public void offer(Task task){
        lock.lock();
        //临界区代码
        try{
            //判断队列的界限  当队列已满
            while (deque.size() == queueSize){
                log.debug("队列已满----阻塞{}",task.getTaskName());
                full.await();
            }
            log.debug("队列未满----可以入队{}",task.getTaskName());
            //生产入队
            deque.addLast(task);
            //唤醒消费
            empty.signalAll();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

    /**
     * 从队列中取出一个任务
     * @return
     */
    public Task take(){
        lock.lock();
        try{
            log.debug("取出一个任务");
            while (deque.size()==0){
                log.debug("队列中没有任务了");
                log.debug("永久阻塞---------");
                empty.await();
            }
            //消费出队
            Task task = deque.removeFirst();
            //唤醒生产
            full.signalAll();
            return task;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }

    //超时阻塞
    public Task poll(long nanos) throws InterruptedException {
        lock.lockInterruptibly();
        try{
            log.debug("取出一个任务");
            while (deque.size()==0){
                log.debug("队列中没有任务了");
                log.debug("超时阻塞--------等待3秒");
                if(nanos<=0)
                    return null;
                //当被打断时 awaitNanos 返回剩余时间
                nanos = empty.awaitNanos(nanos);
            }
            //消费出队
            Task task = deque.removeFirst();
            //唤醒生产
            full.signalAll();
            return task;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
        return null;
    }
}

自定义任务 主要为了打印任务名

@Slf4j(topic = "marx")
public class Task implements Runnable{

    private String taskName;

    public Task(String taskName) {
        this.taskName = taskName;
    }

    public String getTaskName() {
        return taskName;
    }

    @Override
    public void run() {
        log.debug("【{}】任务执行中-----",taskName);
    }
}

日常使用线程池

/**
 * 最大线程和核心线程为1  等于单线程
 * 和自己new线程 || Executors.newFixedThreadPool(1) 区别是什么?
 *  1.newSingleThreadExecutor 保证了单个线程
 *  newFixedThreadPool(1) 可以改变线程池大小,newSingleThreadExecutor不能改变
 *  2.如果是创建单线程串行执行,抛出异常会终止。而采用线程池的话,异常线程终止,
 *  将会启动新的线程继续执行,但是线程池中的线程数始终保持一条
 */
@Slf4j(topic = "marx")
public class Test {

    public static void main(String[] args) {
        //newFixedThreadPool(1) 可以通过强转为ThreadPoolExecutor 调用setCorePoolSize 改变线程池大小
        ExecutorService executorService = Executors.newFixedThreadPool(1);
        ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService;
        threadPoolExecutor.setCorePoolSize(3);
        
        //单线程池执行 任务二发生错误后仍然执行任务三
        ExecutorService executor = Executors.newSingleThreadExecutor();
        executor.execute(()->{
            log.debug("执行任务一");
        });
        executor.execute(()->{
            log.debug("执行任务二");
            System.err.println(100 / 0);
        });
        executor.execute(()->{
            log.debug("执行任务三");
        });
    }
}

image.png

newFixedThreadPool 采用的是LinkedBlockingQueue队列,队列最大值为Integer.MAX_VALUE
例如:Executors.newFixedThreadPool(100);
每产生一个任务创建一个线程,这个线程执行完任务并不会销毁。假设任务超过设定值100,那么线程池中会保留100核心线程,可用于控制最大并发量场景。

public static ExecutorService newFixedThreadPool(int nThreads) {
    return new ThreadPoolExecutor(nThreads, nThreads,
                                  0L, TimeUnit.MILLISECONDS,
                                  new LinkedBlockingQueue<Runnable>());
}

newCachedThreadPool 采用的是同步阻塞队列。适用在单个任务执行时间短任务量大业务场景中效率很高。 由于没有设置核心线程,产生任务后放入到阻塞队列中,创建空闲线程去执行,假设同时创建了5个线程执行,部分线程执行完毕后通过线程复用再到队列中取,可以将资源利用率最大化。

/**
 * 1.缓存
 * 2.没有核心线程 60s后没有任务 线程池中的线程全部死亡
 * 3.空闲线程有 1 << 31 - 1 即 Integer.MAX_VALUE
 * 4.同步阻塞队列
 * 5.效率高。场景:任务执行时间短,但是任务非常多
 */
public static ExecutorService newCachedThreadPool() {
    return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                  60L, TimeUnit.SECONDS,
                                  new SynchronousQueue<Runnable>());
}

线程池提交任务

<T> Future<T> submit(Callable<T>task);//接受Feture类型返回值 通过get方法获取,调用get会阻塞
Future<?> submit(Runnable task); //相当于execute
<T>Future<T> submit(Runnable task, T result);//允许传入一个对象用于接收返回结果

阻塞现象演示

ExecutorService executorService = Executors.newFixedThreadPool(1);

Future<String> fetureTeak = executorService.submit(() -> {
    log.debug("1");
    TimeUnit.SECONDS.sleep(1);
    return "success";
});
log.debug("start");
log.debug("result[{}]",fetureTeak.get());
log.debug("end");
Executors.newFixedThreadPool(100);

演示结果

image.png

execute方法用于提交一个任务
invokeAll 方法可提交多个任务;允许接收多个返回值Feture;允许设置超时时间;会阻塞主线程

ExecutorService executorService = Executors.newFixedThreadPool(10);
List<Future<String>> futures = executorService.invokeAll(Arrays.asList(
        () -> {
            log.debug("1");
            return "1";
        },
        () -> {
            log.debug("2");
            return "2";
        },
        () -> {
            log.debug("3");
            return "3";
        },
        () -> {
            log.debug("4");
            return "4";
        }
));
log.debug("main start");
for (Future<String> future : futures) {
    log.debug( future.get());
}
log.debug("main end");

image.png

invokeAny 提交多个任务但不会执行所有任务。一个任务执行结束即返回不再执行其他;会阻塞主线程

ExecutorService executorService = Executors.newFixedThreadPool(10);
Object o = executorService.invokeAny(Arrays.asList(
        () -> {
            TimeUnit.SECONDS.sleep(3);
            log.debug("1");
            return "1";
        },
        () -> {
            TimeUnit.SECONDS.sleep(5);
            log.debug("2");
            return "2";
        },
        () -> {
            TimeUnit.SECONDS.sleep(1);
            log.debug("3");
            return "3";
        },
        () -> {
            TimeUnit.SECONDS.sleep(6);
            log.debug("4");
            return "4";
        }
));
log.debug("main start");
log.debug("result = [{}]",o);
log.debug("main end");

image.png

线程池的停止方法

线程池状态变为SHUTDOWN; 不会接受新任务; 已提交任务继续执行完; 不会阻塞调用线程(main线程)的执行;

 void shutdown();

线程池状态变为STOP; 不会接受新任务; interrupt方式打断正在执行的任务,List接收未执行的任务

List<Runnable> shutdownNow();

调用shutdown后;如果再调用 awaitTermination 则表示主线程在等待指定时间单位后解阻塞。 指定时间内线程池执行完则不再等待;如果指定时间内线程池未执行完,则到指定时间后不再等待。

executorService.awaitTermination(3,TimeUnit.Seconds)