线程池的理解和实践【2】

JDK提供的线程池实现

这里为什么要强调JDK提供呢? 新手要明白一个点 线程池它是一种线程复用的设计思想,他并不是java的一个专用组件。

你可以用其他语言来实现线程池,也可以不用java jdk提供的线程池构建。(比如你功力比较深 你直接原生代码手写一个线程池也是可以的)

这里我们就主要介绍JDK为我们提供的线程构建接口:

ThreadPoolExecutor

我们找一个最全的参数的构造方法
这里面的参数含义很重要 大家一定要理解
在这里插入图片描述

jdk送你的现成的线程池

这时候新手会自然的想到

这么多参数老子一个一个配岂不是累死?

有没有现成儿的 以及配好的 直接可以拿来用的??

答案是有的!

JDK生怕你麻烦

所以它提供了几种现成的线程池实现 这些实现方法在内部是静态的 意味中你直接可以类名.*** 来创建:

     /**
         * 一些常见的jdk提供的线程池
         * */
        ExecutorService es1 = Executors.newFixedThreadPool(3);

        ExecutorService es2 = Executors.newCachedThreadPool();

        ExecutorService es3 = Executors.newScheduledThreadPool(3);
        

Executors.newFixedThreadPool(3):这个方法创建了一个固定大小的线程池,其中包含3个线程。这意味着线程池中始终保持着3个线程的数量,无论是否有任务需要执行。线程池会重用这些线程来处理提交的任务,避免了频繁地创建和销毁线程对象。

Executors.newCachedThreadPool():这个方法创建了一个缓存线程池,它根据需要自动调整线程数量。如果有可用的空闲线程,则会重用它们来执行新任务。如果没有可用的线程,将创建一个新的线程。这样可以根据任务的数量自动调整线程池的大小,避免了无限制地创建大量线程的问题。

Executors.newScheduledThreadPool(3):这个方法创建了一个定时线程池,它可以安排延迟执行或定期执行的任务。该线程池具有固定大小,包含3个线程。它可以用于需要按计划执行任务的场景,例如定时任务、周期性任务等。线程池会重复使用这些线程来执行计划任务,而不是每次都创建新的线程。

自定义线程池

既然现成的都有了 为什么还要自定义???

这个问题对大佬们来说 很简单 但是对第一次接触多线程的新手来说会困惑。

我们还是坚持 《照顾新手》 原则

自定义线程池大小的需求可能源于特定的应用场景和性能考虑。尽管Java提供了一些现成的线程池实现,但在某些情况下,自定义线程池大小可能更适合满足应用程序的需求。

自定义的线程池我们可以自由的设计 每一个参数 按我们想要的 实现定制化。

大家平时应该打过 LOL CSGO 星际争霸 之类的。

我们打游戏的时候 经常干一件事情 就是改键 把一些系统默认的按键改成我们自己顺手的按键 (有些LOL 的adc 喜欢把走和a改成走a一体化)

线程池也是同样的道理 它给你现成的 但是也允许你自定义。

 /**
         * 自定义线程池
         * */
        ExecutorService es4 =new ThreadPoolExecutor(
                5,//核心线程数
                6,//最大线程数
                30,//存活时间
                TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3));
    }

这里就创建了一个自定义线程池了,它本质上各种功能和上面那个三个没啥区别

这里核心线程数 最大线程数 存活时间和单位 是基础的。

然是是阻塞队列的选择:
无界队列(无限容量队列):无界队列在没有明确的容量限制的情况下存储任务。它可以容纳任意数量的任务,并且不会拒绝任何任务的提交。在使用无界队列时,如果线程池的线程数已达到核心线程数,并且所有线程都在执行任务,则新提交的任务会被放入无界队列中等待执行。常见的无界队列实现是LinkedBlockingQueue,它使用链表来实现队列,没有固定的容量限制。

有界队列(有限容量队列):有界队列有一个固定的容量限制,只能存储有限数量的任务。当线程池的线程数已达到核心线程数,并且所有线程都在执行任务时,如果有新的任务提交,如果队列未满,则新任务会被放入队列中等待执行;如果队列已满,则根据线程池的拒绝策略来处理任务。常见的有界队列实现是ArrayBlockingQueue,它使用数组来实现队列,具有固定的容量。

然后还有拒绝策略:
拒绝策略(rejectedExecutionHandler):当线程池无法接受新的任务时,采取的处理策略。常见的策略包括抛出异常(AbortPolicy)、丢弃任务并抛出异常(DiscardPolicy)、丢弃队列中最早的任务并尝试重新提交(DiscardOldestPolicy)等。

常用方法

ok 我们现在 线程池的创建算是完成了。

我们得到了一个 ExecutorService es 线程池对象。

它有哪些常用方法呢?


execute() 最简单 最直接 直接交给线程池运行

submit(Runnable task):提交一个 Runnable 任务给线程池执行,并返回一个 Future 对象,用于获取任务的执行结果或取消任务的执行。

submit(Callable task):提交一个 Callable 任务给线程池执行,并返回一个 Future 对象,用于获取任务的执行结果或取消任务的执行。Callable 任务可以返回一个结果对象,与 Runnable 不同。

shutdown():优雅地关闭线程池,停止接受新的任务,并尝试完成所有已提交的任务。已提交但未执行的任务可以继续执行,已提交且正在执行的任务可以选择等待完成或被中断。

shutdownNow():强制关闭线程池,尝试停止所有正在执行的任务,并返回等待执行的任务列表。该方法会发送中断信号给所有线程。

awaitTermination(long timeout, TimeUnit unit):阻塞当前线程,等待线程池的所有任务完成,或者超过指定的超时时间。返回 true 表示线程池已经完全终止,返回 false 表示等待超时。

isShutdown():判断线程池是否已经调用了 shutdown() 方法。

isTerminated():判断线程池是否已经完全终止,即所有任务都已完成且线程池已关闭。

invokeAny(Collection<? extends Callable> tasks):提交一组 Callable 任务给线程池执行,并返回其中一个任务的执行结果,无论是否成功执行。如果其中一个任务成功返回结果,其他任务会被取消。

invokeAll(Collection<? extends Callable> tasks):提交一组 Callable 任务给线程池执行,并返回一个包含所有任务执行结果的 List<Future> 对象。调用该方法会阻塞,直到所有任务完成。


使用线程池 多个线程打印当前时间戳

public class CustomThreadPool {

    /**
     * 这里我们创建自定义线程池,
     * jdk提供的现成的现场池也是由这个接口实现。但是下面的参数是固定的。
     * 下面自定义的方式 参数我们可以自己定
     * */
    private ExecutorService executorService;

    public CustomThreadPool(){
       executorService = new ThreadPoolExecutor(
               5,
               6,
               30,
               TimeUnit.SECONDS,
                new ArrayBlockingQueue<>(3));

    }

    public void execute(Runnable task){
        executorService.execute(task);
    }
}

定义一个task 也就是线程要具体执行什么任务

public class PrintTimeTask implements Runnable{
    @Override
    public void run() {
        System.out.println("当前线程:"+Thread.currentThread().getName()+"-打印时间:"+System.currentTimeMillis());
    }
}

Test 往里面丢就行了

   PrintTimeTask timeTask = new PrintTimeTask();

        CustomThreadPool customThreadPool = new CustomThreadPool();

        for (int i = 0; i < 4; i++) {
            customThreadPool.execute(timeTask);
        }
当前线程:pool-1-thread-2-打印时间:1684984731360
当前线程:pool-1-thread-1-打印时间:1684984731360
当前线程:pool-1-thread-4-打印时间:1684984731360
当前线程:pool-1-thread-3-打印时间:1684984731360

这里有一个非常重要的概念 新手要理解:
线程池里的线程 默认全部是非守护线程!
就是说 就算主程序结束了 只要线程池里面的线程还没结束 主程序会等待他们结束 而不会强制关闭线程池里面的线程

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值