线程池 - ThreadPoolExecutor

2 篇文章 0 订阅

1. 线程池状态

  1. ThreadPoolExecutor 使用变量ctl定义为AtomicInteger,高 3 位来表示线程池的状态,低 29 位表示线程的任务数量。

  2. 任务转态
    在这里插入图片描述

  3. 状态转换

RUNNING -> SHUTDOWN -> TIDYING -> TERMINATED
RUNNING -> STOP -> TIDYING -> TERMINATED

在这里插入图片描述

  1. 源代码
public class ThreadPoolExecutor extends AbstractExecutorService {

    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
    private static final int COUNT_BITS = Integer.SIZE - 3;
    private static final int COUNT_MASK = (1 << COUNT_BITS) - 1;

    // 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;

    // Packing and unpacking ctl
    private static int runStateOf(int c)     { return c & ~COUNT_MASK; }
    private static int workerCountOf(int c)  { return c & COUNT_MASK; }
    private static int ctlOf(int rs, int wc) { return rs | wc; }
    ...
}

2. 构造方法

  1. 代码
 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;
    }
  1. 参数说明
    在这里插入图片描述
  2. 工作流程
  • 创建线程池后等待提交过来的任务,当一个任务提交给线程池后,线程池会创建一个新线程来执行任务。
  • 当正在运行的线程数小于corePoolSize,直接创建线程执行该任务;当线程数达到 corePoolSize并且没有线程空闲,这时再加入执行的任务,新的任务会进入workQueue 队列排队,直到有空闲的线程。
  • 如果队列为有界队列,当队列满时,会创建 maximumPoolSize - corePoolSize 数目的非核心线程来执行该任务;如果是无界队列则无需考虑创建非核心线程来执行,maximumPoolSize没有意义。
  • 如果队列满了并且线程达到maximumPoolSize值,当再次有新任务时会执行拒绝策略。
  • 超过corePoolSize 的非核心线程如果在一段时间处于空闲状态,这个时候由keepAliveTime 和 unit 来控制回收线程。

3. 线程池

Executors提供一些静态工厂方法,方便地创建预配置的线程池实例
3.1 newFixedThreadPool

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

1. 特点

  • 固定大小的线程池
  • 核心线程数等于最大线程数
  • 阻塞队列是无界的,没有限制
  • 无需创建非核心线程数,空闲时间keepAliveTime为0

2. 说明

  • 适用场景:任务数量已知且相对固定,任务执行时间较长(有效控制并发数量,防止短期任务占用大量资源)

3. 示例

@Slf4j
public class Demo01NewFixedThreadPool {

    public static void main(String[] args) {
        test01();
        // test02();
    }

    public static void test01() {
        // 线程池的线程数为2,循环三次调用线程
        ExecutorService executor = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 3; i++) {
            int j = i;
            executor.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    log.info("{}", j);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }


    /**
     * ThreadFactory -- 线程工厂 - 定义线程创建时的线程名
     */
    public static void test02() {
        // 线程池的线程数为2,循环三次调用线程
        ExecutorService executor = Executors.newFixedThreadPool(2, new ThreadFactory() {
            private final AtomicInteger num = new AtomicInteger(1);
            @Override
            public Thread newThread(Runnable r) {
                return new Thread(r, "pool-demo-t-" + num.getAndIncrement());
            }

        });
        for (int i = 0; i < 3; i++) {
            int j = i;
            executor.execute(() -> {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    log.info("{}", j);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });
        }

        // 关闭线程池
        executor.shutdown();
    }
}

3.2 newCachedThreadPool

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

1. 特点

  • 可缓存线程池
  • 阻塞队列是无界的
  • 核心线程数为0,最大线程数为Integer.MAX_VALUE,创建的线程都为非核心线程数
  • keepAliveTime为60s,线程池中的线程在一段时间内(默认60秒)没有被使用将被终止移除

2. 说明

  • 适合处理大量短期的(异步)任务,任务执行时间较短。

3. 示例

@Slf4j
public class Demo03NewCachedThreadPool {
    public static void main(String[] args) {
        test01();
    }

    public static void test01() {
        // 每个任务都会创建一个新的线程执行
        ExecutorService executor = Executors.newCachedThreadPool();
        for (int i = 0; i < 10; i++) {
            int j = i;
            executor.submit(() ->{
                log.info("{}", j);
                try {
                    // 模拟任务执行时间
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            });

        }
        // 关闭线程
        executor.shutdown();
    }

}

3.3 newSingleThreadExecutor

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

1. 特点

  • 单线程的线程池
  • 核心线程数等于最大线程数等于1,唯一的线程不会被释放
  • 阻塞队列是无界的,没有限制

2. 说明

  • 简单任务调用,顺序任务执行

3. 示例

/**
 * newSingleThreadExecutor - 单个worker线程的Executor
 * 线程数固定为1,当任务书多余1时,其余任务进入无界队列
 */
@Slf4j
public class Demo03NewSingleThreadExecutor {


    public static void main(String[] args) {
        test01();
    }

    /**
     * 执行三个线程,当任务执行完才会执行下一个任务
     * 其中有线程任务执行异常时, 会创建一个新的线程执行任务(注意看线程名字)
     */
    public static void test01() {
        ExecutorService executorService = Executors.newSingleThreadExecutor();
        executorService.execute(() -> {
            // 模拟异常情况
            log.debug("1");
            int i = 1 / 0;
        });

        executorService.execute(() -> log.info("2"));

        executorService.execute(() -> log.info("3"));

        executorService.shutdown();
    }
}

4. 任务调度线程池

4.1 ScheduledExecutorService
ScheduledExecutorService是 Java 中用于调度任务的接口,它是ExecutorService的子接口,提供了在给定的延迟后或定期执行任务的方法。通常用于替代传统的 java.util.Timer 和 java.util.TimerTask,因为它提供了更强大的功能和更好的灵活性。通过Executors工具类的静态方法来创建ScheduledExecutorService。
4.2 ScheduledExecutorService的几种任务调度方法

  • schedule(Runnable command, long delay, TimeUnit unit)
    在给定的延迟后执行一次任务。
  • schedule(Callable callable, long delay, TimeUnit unit)
    在给定的延迟后执行一次任务,并返回一个 Future 对象。
  • scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
    在给定的初始延迟后开始执行任务,并以固定的频率重复执行。
  • scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
    在给定的初始延迟后开始执行任务,并在每次执行完成后等待给定的延迟时间再执行下一次任务。
    1. 示例
@Slf4j
public class Demo06ScheduledExecutorService {

    public static void main(String[] args) {
         // test01();
         test02();
         // test03();

    }

    /**
     * 使用schedule执行两个任务,在1s之后执行
     */
    public static void test01() {
        ScheduledExecutorService executor = Executors.newScheduledThreadPool(2);

        executor.schedule(() -> {
            try {
                log.info("task 1, time:{}", new Date());
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, 1000, TimeUnit.MILLISECONDS);

        executor.schedule(() -> {
            log.info("task 2, time:{}", new Date());
        }, 1000, TimeUnit.MILLISECONDS);

        executor.shutdown();
        try {
            if (!executor.awaitTermination(60, TimeUnit.SECONDS)) {
                executor.shutdownNow();
            }
        } catch (InterruptedException e) {
            executor.shutdownNow();
        }

    }

    /**
     * 使用scheduleAtFixedRate执行任务
     * 初始延迟为1秒,但是 任务执行时间 > 间隔时间,之后每隔2秒执行一次
     */
    public static void test02() {
        ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
        scheduler.scheduleAtFixedRate(() -> {
            log.info("begin...");
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, 1000, 1000,  TimeUnit.MILLISECONDS);
    }
    
    /**
     * 使用scheduleWithFixedDelay执行任务
     * 初始延迟为1秒,任务执行时间为3s > 间隔时间为2s,任务延迟时间为执行时间+间隔时间
     */
    public static void test03() {
        ScheduledExecutorService executorService = Executors.newScheduledThreadPool(1);
        executorService.scheduleWithFixedDelay(() -> {
            try {
                log.info("begin...");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }, 1000, 2000, TimeUnit.MILLISECONDS);
    }
}

2. 说明
资源管理:使用 ScheduledThreadPoolExecutor 时,需要注意正确关闭执行器,以释放系统资源。可以使用 shutdown() 方法来关闭执行器。
异常处理:如果任务抛出未捕获的异常,默认情况下会终止周期性任务的执行。可以通过设置 ThreadFactory 或 RejectedExecutionHandler 来处理异常。
任务间隔:scheduleAtFixedRate 和 scheduleWithFixedDelay 的区别在于前者是以固定的时间间隔执行任务,而后者是每次任务执行完后再延迟指定时间执行

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值