并发学习记录14:线程池

线程的接口以及实现类

线程池的状态

ThreadPoolExcutor使用int的高三位来表示线程池的状态,低29位表示线程数量

running状态:高三位的值是111,可以接受新任务和处理阻塞队列的任务
shutdown状态:高三位是000,不会再接受新的任务,但是阻塞队列的任务会处理完,可以说是安全关闭线程池的概念
stop状态:高三位是001,不会接收新的任务,连正在执行的任务都会中断,并且会抛弃阻塞队列中的任务
tidying状态:高三位是010,任务全部执行完毕,活动线程为0,即将进入终结
terminated状态:高三位011,终结状态

这些信息都会被存储在一个原子变量ctl中,目的是将线程池和线程个数统一起来,一次cas原子操作就可以同时赋值线程池状态和线程个数。
源码:

// runState is stored in the high-order bits
    private static final int RUNNING    = -1 << COUNT_BITS;
    private static final int SHUTDOWN   =  0 << COUNT_BITS;
    private static final int STOP       =  1 << COUNT_BITS;
    private static final int TIDYING    =  2 << COUNT_BITS;
    private static final int TERMINATED =  3 << COUNT_BITS;

//一个原子变量同时标记线程池状态和线程数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));

构造方法

线程池的构造方法

    /**
     * Creates a new {@code ThreadPoolExecutor} with the given initial
     * parameters.
     *
     * @param corePoolSize the number of threads to keep in the pool, even
     *        if they are idle, unless {@code allowCoreThreadTimeOut} is set
     * @param maximumPoolSize the maximum number of threads to allow in the
     *        pool
     * @param keepAliveTime when the number of threads is greater than
     *        the core, this is the maximum time that excess idle threads
     *        will wait for new tasks before terminating.
     * @param unit the time unit for the {@code keepAliveTime} argument
     * @param workQueue the queue to use for holding tasks before they are
     *        executed.  This queue will hold only the {@code Runnable}
     *        tasks submitted by the {@code execute} method.
     * @param threadFactory the factory to use when the executor
     *        creates a new thread
     * @param handler the handler to use when execution is blocked
     *        because the thread bounds and queue capacities are reached
     * @throws IllegalArgumentException if one of the following holds:<br>
     *         {@code corePoolSize < 0}<br>
     *         {@code keepAliveTime < 0}<br>
     *         {@code maximumPoolSize <= 0}<br>
     *         {@code maximumPoolSize < corePoolSize}
     * @throws NullPointerException if {@code workQueue}
     *         or {@code threadFactory} or {@code handler} is null
     */
    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)

corePoolSize:核心线程数目,就是最多保留的线程数
maximumPoolSize:最大线程数目
keepAliveTime:救急线程的生存时间
unit:救急线程生存时间的时间单位
workQueue:阻塞队列
threadFactory:线程工厂,可以为线程创建个好名字
handler:拒绝策略

工作方式:

假设核心线程数目为2,最大线程数目为3。
1.线程池中刚开始时没有线程,当第一个任务提交给线程池后,线程池会创建一个新的线程来执行任务
2.当线程数达到核心线程数(一般就是cpu的核心数)并且没有线程空闲,这时候再加入任务,新加的任务会被加入阻塞队列排队,直到有空闲概念的线程
3.如果队列选择了有界队列,那么当任务超过了队列大小时,会创建最大线程数目-核心线程数目个线程来救急
4.如果线程达到了最大线程数目,这时如果有新任务来,会执行拒绝策略。
5.一些典型的拒绝策略如下:
AbortPolicy会让调用者抛出异常
CallerRunsPolicy 让调用者运行任务
DiscardPolicy 放弃本次任务
DiscardOldestPolicy 放弃队列中最早的任务,本任务取而代之
Dubbo 的实现,在抛出 RejectedExecutionException 异常之前会记录日志,并 dump 线程栈信息,方便定位问题

Netty 的实现,是创建一个新线程来执行任务
ActiveMQ 的实现,带超时等待(60s)尝试放入队列,类似我们之前自定义的拒绝策略
6.当高峰过去后,超过核心线程数目的救急线程如果一段时间没用任务做,那么经过 keepAliveTime 个 timeUnit的时间,救急线程需要结束节省资源

一些常见的线程池

newFixedThreadPool

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

特点是:
核心线程数=最大线程数,不用创建救急线程,因此也不需要设置超时时间
阻塞队列是无界的,可以放任意数量的任务

newCachedThreadPool

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

特点:
核心线程数是0,最大线程数是 Integer.MAX_VALUE,救急线程的空闲生存时间是60s
核心线程数为0,就是没有核心线程,全部都是救急线程,生存周期是60s,救急线程可以无限创建
队列采用了synchronizedQueue实现,特点是没有容量,如果没有线程来取任务,交付任务的线程就会阻塞住
这个线程池表现为线程数会根据任务量不断增长,没有上限,当任务执行完毕,空闲一分钟后就可以释放线程。适合任务数比较密集,但是每个任务执行时间都比较短的情况

newSingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

多个任务排队执行。线程数固定为1,任务数多于1时,会放入无界队列排队,任务执行完毕,唯一的线程也不会被释放。

这个线程池和自己创建单线程执行任务的区别:
自己创建一个单线程执行任务,如果任务执行失败会影响后续任务执行,而线程池还会新建一个线程,保证池的正常工作
另外:
Executors.newSingleThreadExecutor()的线程个数始终为1,不能修改,FinalizableDelegatedExecutorService 应用的是装饰器模式(见https://www.cnblogs.com/wbstudy/p/16750131.html),只对外暴露了 ExecutorService 接口,因此不能调用 ThreadPoolExecutor 中特有的方法

Executors.newFixedThreadPool(1) 初始时为1,以后还可以修改,对外暴露的是 ThreadPoolExecutor 对象,可以强转后调用setCorePoolSize 等方法进行修改

任务的提交

//执行任务的方法,ThreadPoolExecutor类中
public void execute(Runnable command)

//AbstractExecutorService类中
//提交任务task,用返回值future获得任务执行结果
<T> Future<T> submit(Callable<T> task);

// 提交 tasks 中所有任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
 throws InterruptedException;

// 提交 tasks 中所有任务,带超时时间
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
 long timeout, TimeUnit unit)
 throws InterruptedException;

// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消
<T> T invokeAny(Collection<? extends Callable<T>> tasks)
 throws InterruptedException, ExecutionException;

// 提交 tasks 中所有任务,哪个任务先成功执行完毕,返回此任务执行结果,其它任务取消,带超时时间
<T> T invokeAny(Collection<? extends Callable<T>> tasks,
 long timeout, TimeUnit unit)
 throws InterruptedException, ExecutionException, TimeoutException;

代码的测试:

import lombok.extern.slf4j.Slf4j;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.*;

@Slf4j(topic = "ch.ThreadPoolTest03")
public class ThreadPoolTest03 {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
//        method2();
        method3();
    }

    //测试submit
    public static void method1() {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        Future<String> result01 = threadPool.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.debug("任务一");
                Thread.sleep(1000);
                log.debug("1over");
                return "任务一执行结束";
            }
        });
        Future<String> result02 = threadPool.submit(() -> {
            log.debug("任务二");
            Thread.sleep(2000);
            log.debug("2over");
            return "任务二执行结束";
        });
        threadPool.submit(new Callable<String>() {
            @Override
            public String call() throws Exception {
                log.debug("任务三");
                Thread.sleep(3000);
                log.debug("3over");
                return "任务三执行结束";
            }
        });
        log.debug("线程池关闭");
        threadPool.shutdown();
    }

    //测试invokeAll
    public static void method2() throws InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        List<Future<String>> futures = threadPool.invokeAll(Arrays.asList(
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        log.debug("任务一");
                        Thread.sleep(1000);
                        log.debug("任务一完成");
                        return "扎";
                    }
                },
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        log.debug("任务二");
                        Thread.sleep(2000);
                        log.debug("任务二完成");
                        return "西";
                    }
                },
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        log.debug("任务三");
                        Thread.sleep(3000);
                        log.debug("任务三完成");
                        return "得勒";
                    }
                }
        ));
        futures.stream().forEach(f -> {
            try {
                log.debug("结果是{}", f.get());
            } catch (InterruptedException | ExecutionException e) {
                throw new RuntimeException(e);
            }
        });
    }

    //测试invokeAny
    public static void method3() throws ExecutionException, InterruptedException {
        ExecutorService threadPool = Executors.newFixedThreadPool(3);
        //预期是返回最早结束的任务执行结果
        String result = threadPool.invokeAny(Arrays.asList(
                () -> {
                    log.debug("任务一");
                    Thread.sleep(1000);
                    log.debug("任务一结束");
                    int a = 1 / 0;
                    return "扎";
                },
                () -> {
                    log.debug("任务二");
                    Thread.sleep(2000);
                    log.debug("任务二结束");
                    return "西";
                },
                () -> {
                    log.debug("任务三");
                    Thread.sleep(3000);
                    log.debug("任务三结束");
                    return "得勒";
                }
        ));
        log.debug("最早顺利结束的任务结果是{}", result);
    }
}

关闭线程池

shutdown方法,线程池状态变成shutdown,不会接收新的任务,但是已经提交的任务会执行完,此方法不会阻塞调用线程的执行:

/**
     * Initiates an orderly shutdown in which previously submitted
     * tasks are executed, but no new tasks will be accepted.
     * Invocation has no additional effect if already shut down.
     *
     * <p>This method does not wait for previously submitted tasks to
     * complete execution.  Use {@link #awaitTermination awaitTermination}
     * to do that.
     *
     * @throws SecurityException {@inheritDoc}
     */
    public void shutdown() {
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(SHUTDOWN);
            interruptIdleWorkers();
            onShutdown(); // hook for ScheduledThreadPoolExecutor
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
    }

shutdownNow方法,线程池状态变为STOP,不会再接收新的任务,会将阻塞队列中的任务返回,并用interrupt方法中断正在执行的任务:

    /**
     * Attempts to stop all actively executing tasks, halts the
     * processing of waiting tasks, and returns a list of the tasks
     * that were awaiting execution. These tasks are drained (removed)
     * from the task queue upon return from this method.
     *
     * <p>This method does not wait for actively executing tasks to
     * terminate.  Use {@link #awaitTermination awaitTermination} to
     * do that.
     *
     * <p>There are no guarantees beyond best-effort attempts to stop
     * processing actively executing tasks.  This implementation
     * cancels tasks via {@link Thread#interrupt}, so any task that
     * fails to respond to interrupts may never terminate.
     *
     * @throws SecurityException {@inheritDoc}
     */
    public List<Runnable> shutdownNow() {
        List<Runnable> tasks;
        final ReentrantLock mainLock = this.mainLock;
        mainLock.lock();
        try {
            checkShutdownAccess();
            advanceRunState(STOP);
            interruptWorkers();
            tasks = drainQueue();
        } finally {
            mainLock.unlock();
        }
        tryTerminate();
        return tasks;
    }

测试一下shutdown和shutdownNow:

import lombok.extern.slf4j.Slf4j;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

@Slf4j(topic = "ch.ThreadPoolTest04")
public class ThreadPoolTest04 {
    public static void main(String[] args) {
        List<Callable<String>> callableTasks = Arrays.asList(
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        log.debug("任务一");
                        Thread.sleep(1000);
                        log.debug("任务一结束");
                        return "扎";
                    }
                },
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        log.debug("任务二");
                        Thread.sleep(2000);
                        log.debug("任务二结束");
                        return "不多";
                    }
                },
                new Callable<String>() {
                    @Override
                    public String call() throws Exception {
                        log.debug("任务三");
                        Thread.sleep(3000);
                        log.debug("任务三结束");
                        return "得勒";
                    }
                });
//        method1(callableTasks);
        method2(callableTasks);
    }

    //测试shutdown
    public static void method1(List<Callable<String>> callables) {
        if (callables == null || callables.size() == 0) return;
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        ArrayList<Future> futures = new ArrayList<>();
        for (int i = 0; i < callables.size(); i++) {
            Future submit = threadPool.submit(callables.get(i));
            futures.add(submit);
        }
        log.debug("shutdown喽");
        threadPool.shutdown();
    }

    //测试shutdownNow
    public static void method2(List<Callable<String>> callables) {
        if (callables == null || callables.size() == 0) return;
        ExecutorService threadPool = Executors.newFixedThreadPool(2);
        ArrayList<Future> futures = new ArrayList<>();
        for (int i = 0; i < callables.size(); i++) {
            Future submit = threadPool.submit(callables.get(i));
            futures.add(submit);
        }
        log.debug("shutdown喽");
        //预期返回任务三
        List<Runnable> noDos = threadPool.shutdownNow();
        log.debug("已经shutdown");
        //返回的是阻塞队列中的任务,而被打断的任务就遗失了相当于
        noDos.stream().forEach(f -> {
            f.run();
        });
    }
}
posted @   理塘DJ  阅读(37)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示