ExecutorService线程池

前言

        在我们的日常开发中,难免会使用到线程,部分还会用到多线程并发问题。我们知道,线程的创建和释放,需要占用不小的内存和资源。如果每次需要使用线程时,都new 一个Thread的话,难免会造成资源的浪费,而且可以无限制创建,之间相互竞争,会导致过多占用系统资源导致系统瘫痪。不利于扩展,比如如定时执行、定期执行、线程中断,所以很有必要了解下ExecutorService的使用。

        ExecutorService是Java提供的线程池,也就是说,每次我们需要使用线程的时候,可以通过ExecutorService获得线程。它可以有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞,同时提供定时执行、定期执行、单线程、并发数控制等功能,也不用使用TimerTask了。
1.ExecutorService的创建方式

 public ThreadPoolExecutor(int corePoolSize,
                                  int maximumPoolSize,
                                  long keepAliveTime,
                                  TimeUnit unit,
                                  BlockingQueue<Runnable> workQueue,
                                  ThreadFactory threadFactory,
                                  RejectedExecutionHandler handler)

所有线程池最终都是通过这个方法来创建的。

corePoolSize : 核心线程数,一旦创建将不会再释放。如果创建的线程数还没有达到指定的核心线程数量,将会继续创建新的核心线程,直到达到最大核心线程数后,核心线程数将不在增加;如果没有空闲的核心线程,同时又未达到最大线程数,则将继续创建非核心线程;如果核心线程数等于最大线程数,则当核心线程都处于激活状态时,任务将被挂起,等待空闲线程来执行。

maximumPoolSize : 最大线程数,允许创建的最大线程数量。如果最大线程数等于核心线程数,则无法创建非核心线程;如果非核心线程处于空闲时,超过设置的空闲时间,则将被回收,释放占用的资源。

keepAliveTime : 也就是当线程空闲时,所允许保存的最大时间,超过这个时间,线程将被释放销毁,但只针对于非核心线程。

unit : 时间单位,TimeUnit.SECONDS等。

workQueue : 任务队列,存储暂时无法执行的任务,等待空闲线程来执行任务。

threadFactory :  线程工程,用于创建线程。

handler : 当线程边界和队列容量已经达到最大时,用于处理阻塞时的程序
2.线程池的类型
2.1 可缓存线程池

ExecutorService cachePool = Executors.newCachedThreadPool();

   看看它的具体创建方式:

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

    通过它的创建方式可以知道,创建的都是非核心线程,而且最大线程数为Interge的最大值,空闲线程存活时间是1分钟。如果有大量耗时的任务,则不适该创建方式。它只适用于生命周期短的任务。
2.2 单线程池

ExecutorService singlePool = Executors.newSingleThreadExecutor();

顾名思义,也就是创建一个核心线程:  

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

只用一个线程来执行任务,保证任务按FIFO顺序一个个执行。
2.3 固定线程数线程池

Executors.newFixedThreadPool(3);

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

也就是创建固定数量的可复用的线程数,来执行任务。当线程数达到最大核心线程数,则加入队列等待有空闲线程时再执行。
2.4 固定线程数,支持定时和周期性任务

ExecutorService scheduledPool = Executors.newScheduledThreadPool(5);

    public ScheduledThreadPoolExecutor(int corePoolSize) {
        super(corePoolSize, Integer.MAX_VALUE,
              DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
              new DelayedWorkQueue());
    }

可用于替代handler.postDelay和Timer定时器等延时和周期性任务。 

public ScheduledFuture<?> schedule(Runnable command,
                                           long delay, TimeUnit unit);
  public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
                                                      long initialDelay,
                                                      long period,
                                                      TimeUnit unit);
  public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
                                                         long initialDelay,
                                                         long delay,
                                                         TimeUnit unit);

scheduleAtFixedRate和sheduleWithFixedDelay有什么不同呢?

scheduleAtFixedRate:创建并执行一个在给定初始延迟后的定期操作,也就是将在 initialDelay 后开始执行,然后在initialDelay+period 后下一个任务执行,接着在 initialDelay + 2 * period 后执行,依此类推 ,也就是只在第一次任务执行时有延时。

sheduleWithFixedDelay:创建并执行一个在给定初始延迟后首次启用的定期操作,随后,在每一次执行终止和下一次执行开始之间都存在给定的延迟,即总时间是(initialDelay +  period)*n
2.4 手动创建线程池 

 private ExecutorService pool = new ThreadPoolExecutor(3, 10,
                10L, TimeUnit.SECONDS,
                new LinkedBlockingQueue<Runnable>(512), Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.AbortPolicy());

可以根据自己的需求创建指定的核心线程数和总线程数。
3. 如何终止一个周期性任务呢?

直接上源码你就懂了:  

public final class LgExecutorService {
     
        private ConcurrentHashMap<String, Future> futureMap = new ConcurrentHashMap<>();
     
        private ScheduledExecutorService executorService = new ScheduledThreadPoolExecutor(5);
     
     
        private LgExecutorService() {
     
        }
     
        private static final class InnerExecutorService {
            private static final LgExecutorService INSTANCE = new LgExecutorService();
        }
     
        public static LgExecutorService getInstance() {
            return InnerExecutorService.INSTANCE;
        }
     
        public ConcurrentHashMap<String, Future> getFutureMap() {
            return futureMap;
        }
     
        public void execute(Runnable runnable) {
            if (runnable != null) {
                executorService.execute(runnable);
            }
        }
     
        /**
         * @param runnable
         * @param delay    延迟时间
         * @param timeUnit 时间单位
         */
        public void sheduler(Runnable runnable, long delay, TimeUnit timeUnit) {
            if (runnable != null) {
                executorService.schedule(runnable, delay, timeUnit);
            }
        }
     
        /**
         * 执行延时周期性任务
         *
         * @param runnable {@code LgExecutorSercice.JobRunnable}
         * @param initialDelay 延迟时间
         * @param period       周期时间
         * @param timeUnit     时间单位
         */
        public <T extends JobRunnable> void sheduler(T runnable, long initialDelay, long period, TimeUnit timeUnit) {
            if (runnable != null) {
                Future future = executorService.scheduleAtFixedRate(runnable, initialDelay, period, timeUnit);
                futureMap.put(runnable.getJobId(), future);
            }
        }
     
        public static abstract class JobRunnable implements Runnable {
     
            private String jobId;
     
            public JobRunnable(@NonNull String jobId) {
                this.jobId = jobId;
            }
     
            /**
             * 强制终止定时线程
             */
            public void terminal() {
                try {
                    Future future = LgExecutorService.getInstance().getFutureMap().remove(jobId);
                    future.cancel(true);
                } finally {
                    System.out.println("jobId " + jobId + " had cancel");
                }
            }
     
            public String getJobId() {
                return jobId;
            }
     
        }
        
    }

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
你好!对于基于 ExecutorService 线程池工具类的问题,我可以给你一些基本的解释和用法示例。 ExecutorService 是 Java 提供的一个线程池工具类,它可以帮助我们管理和执行线程任务。它的主要作用是重用线程,减少线程创建和销毁的开销,提高程序的性能。 首先,我们需要创建一个 ExecutorService 实例。可以使用 Executors 类的静态方法来创建不同类型的线程池,比如: ```java ExecutorService executorService = Executors.newFixedThreadPool(5); ``` 上面的代码创建了一个固定大小为 5 的线程池。你也可以根据需求选择其他类型的线程池,比如 CachedThreadPool、SingleThreadExecutor 等。 接下来,我们可以向线程池提交任务。可以通过调用 `submit()` 方法来提交一个 Callable 或 Runnable 对象,例如: ```java executorService.submit(new Callable<String>() { @Override public String call() throws Exception { // 在这里编写需要执行的任务逻辑 return "任务执行结果"; } }); ``` 或者使用 Lambda 表达式简化代码: ```java executorService.submit(() -> { // 在这里编写需要执行的任务逻辑 return "任务执行结果"; }); ``` 提交任务后,线程池会自动分配一个可用的线程去执行任务,并返回一个 Future 对象,可以用来获取任务的执行结果。 最后,当我们不再需要线程池时,可以调用 `shutdown()` 方法来关闭线程池: ```java executorService.shutdown(); ``` 这会等待已提交的任务执行完毕,并停止接受新的任务。 以上就是基于 ExecutorService 线程池工具类的基本用法。希望对你有所帮助!如果你还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值