多线程学习之线程池

Love The Way You Lie 2024-03-24 22:28 138阅读 0赞

线程状态


































线程状态 具体含义
NEW 一个尚未启动的线程的状态。也称之为初始、开始状态。线程刚被创建,但是并未启动。还没调用start方法。MyThread t = new MyThread()只有线程对象,没有线程特征。
RUNNABLE 当我们调用线程对象的start方法,那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程,线程一经启动并不是立即得到执行,线程的运行与否要听令与CPU的调度,那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格,但是并没有真正的执行起来而是在等待CPU的度。
BLOCKED 当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态。
WAITING 一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种,分别是调用Object.wait()、join()方法。处于等待状态的线程,正在等待其他线程去执行一个特定的操作。例如:因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll();一个因为join()而等待的线程正在等待另一个线程结束。
TIMED_WAITING 一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种,分别是:Thread.sleep(long),Object.wait(long)、join(long)。
TERMINATED 一个完全运行完成的线程的状态。也称之为终止状态、结束状态

f8e4e34057d84039921a3b61832c6e86.png

线程池基本原理和设计思想

基本原理:

线程池可以看做成一个池子,在该池子中存储很多个线程。

线程池存在的意义:

系统创建一个线程涉及到与操作系统交互因此成本是比较高的,当程序中需要创建大量生存期很短暂的线程时,频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系统资源的消耗。针对这一种情况,为了提高性能,我们就可以采用线程池。线程池在启动的时,会创建大量空闲线程,当我们向线程池提交任务的时,线程池就会启动一个线程来执行该任务。等待任务执行完毕以后,线程并不会死亡,而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。

设计思想:

  1. 准备一个任务容器
  2. 一次性启动多个(2个)消费者线程
  3. 刚开始任务容器是空的,所以线程都在wait
  4. 直到一个外部线程向这个任务容器中扔了一个”任务”,就会有一个消费者线程被唤醒
  5. 这个消费者线程取出”任务”,并且执行这个任务,执行完毕后,继续等待下一次任务的到来

#

方法:

Executors —- 可以帮助我们创建线程池对象
ExecutorService —- 可以帮助我们控制线程池

创建线程池-Executors默认线程池

创建一个默认的线程池

  1. /**
  2. * @Author:kkoneone11
  3. * @name:MyThreadPoolDemo
  4. * @Date:2023/8/28 9:30
  5. */
  6. public class MyThreadPoolDemo {
  7. public static void main(String[] args) {
  8. ExecutorService executorService = Executors.newCachedThreadPool();
  9. executorService.submit(() -> {
  10. System.out.println(Thread.currentThread().getName() + "在执行");
  11. });
  12. executorService.submit(() -> {
  13. System.out.println(Thread.currentThread().getName() + "在执行");
  14. });
  15. executorService.shutdown();
  16. }
  17. }

创建线程池-Executors创建指定上限的线程池

创建一个指定的最多线程数量的线程池

  1. public class MyThreadPoolDemo2 {
  2. public static void main(String[] args) {
  3. //参数不是初始值而是最大值
  4. ExecutorService executorService = Executors.newFixedThreadPool(10);
  5. ThreadPoolExecutor pool = (ThreadPoolExecutor) executorService;
  6. System.out.println(pool.getPoolSize());//0
  7. executorService.submit(()->{
  8. System.out.println(Thread.currentThread().getName() + "在执行");
  9. });
  10. executorService.submit(()->{
  11. System.out.println(Thread.currentThread().getName() + "在执行");
  12. });
  13. System.out.println(pool.getPoolSize());//2
  14. // executorService.shutdown();
  15. }
  16. }

线程池参数

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(核心线程数量,最大线程数量,空闲线程最大存活时间,任务队列,创建线程工厂,任务的拒绝策略);

9d238a3329904268a76625d495dab45e.png

  1. public ThreadPoolExecutor(int corePoolSize,
  2. int maximumPoolSize,
  3. long keepAliveTime,
  4. TimeUnit unit,
  5. BlockingQueue<Runnable> workQueue,
  6. ThreadFactory threadFactory,
  7. RejectedExecutionHandler handler)
  8. corePoolSize 核心线程的最大值,不能小于0
  9. maximumPoolSize:最大线程数,不能小于等于0maximumPoolSize >= corePoolSize
  10. keepAliveTime 空闲线程最大存活时间,不能小于0
  11. unit 时间单位
  12. workQueue 任务队列,不能为null
  13. threadFactory 创建线程工厂,不能为null
  14. handler 任务的拒绝策略,不能为null

线程工厂

线程工厂是一个接口,名为ThreadFactory。它只有一个方法newThread(Runnable r),用于创建新的线程。你可以实现这个接口来自定义线程的创建.使用一个AtomicInteger来生成唯一的线程ID,然后将这个ID用于线程的名字

  1. public class SimpleThreadFactory implements ThreadFactory {
  2. private final AtomicInteger threadId = new AtomicInteger(1);
  3. @Override
  4. public Thread newThread(Runnable r) {
  5. Thread thread = new Thread(r);
  6. thread.setName("MyThread-" + threadId.getAndIncrement());
  7. return thread;
  8. }
  9. }

创建线程池

  1. ThreadPoolExecutor executor = new ThreadPoolExecutor(
  2. 5, 10, 60L, TimeUnit.SECONDS,
  3. new LinkedBlockingQueue<>(),
  4. new SimpleThreadFactory()
  5. );

#

非默认任务拒绝策略

RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口,它下面存在4个子类

  • ThreadPoolExecutor.AbortPolicy: 丢弃任务并抛出RejectedExecutionException异常。是默认的策略。
  • ThreadPoolExecutor.DiscardPolicy: 丢弃任务,但是不抛出异常 这是不推荐的做法。
  • ThreadPoolExecutor.DiscardOldestPolicy: 抛弃队列中等待最久的任务 然后把当前任务加入队列中。
  • ThreadPoolExecutor.CallerRunsPolicy: 调用任务的run()方法绕过线程池直接执行。

注意:明确线程池对多可执行的任务数 = 任务容器容量 + 最大线程数

ThreadPoolExecutor.AbortPolicy 实例1:

  1. public class MyThreadPoolDemo {
  2. public static void main(String[] args) {
  3. /*
  4. * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
  5. */
  6. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 3, 20,
  7. TimeUnit.SECONDS, new ArrayBlockingQueue<>(1), Executors.defaultThreadFactory(),
  8. new ThreadPoolExecutor.AbortPolicy());
  9. // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用AbortPolicy这个任务处理策略的时候,就会抛出异常
  10. for(int x = 0; x<5; x++){
  11. threadPoolExecutor.submit(()->{
  12. System.out.println(Thread.currentThread().getName() +"---》正在执行");
  13. });
  14. }
  15. }
  16. }

有一个任务被抛弃且抛出异常

febf378d437b4153821637c12962fff5.png

ThreadPoolExecutor.DiscardPolicy 实例2:

  1. public class ThreadPoolExecutorDemo {
  2. public static void main(String[] args) {
  3. /**
  4. * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
  5. */
  6. ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
  7. new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardPolicy()) ;
  8. // 提交5个任务,而该线程池最多可以处理4个任务,当我们使用DiscardPolicy这个任务处理策略的时候,控制台不会报错
  9. for(int x = 0 ; x < 5 ; x++) {
  10. threadPoolExecutor.submit(() -> {
  11. System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
  12. });
  13. }
  14. }
  15. }

丢弃了一个任务但没有报错

116f084508154de8bb0ea88f9cab39ad.png

ThreadPoolExecutor.DiscardOldestPolicy 实例3:

  1. public class ThreadPoolExecutorDemo {
  2. public static void main(String[] args) {
  3. /**
  4. * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
  5. */
  6. ThreadPoolExecutor threadPoolExecutor;
  7. threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
  8. new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.DiscardOldestPolicy());
  9. // 提交5个任务
  10. for(int x = 0 ; x < 5 ; x++) {
  11. // 定义一个变量,来指定指定当前执行的任务;这个变量需要被final修饰
  12. final int y = x ;
  13. threadPoolExecutor.submit(() -> {
  14. System.out.println(Thread.currentThread().getName() + "---->> 执行了任务" + y);
  15. });
  16. }
  17. }
  18. }

1de05ba170ee422b9f219f5b0a3dddbf.png

ThreadPoolExecutor.CallerRunsPolicy 实例4:

  1. public class MyThreadPoolDemo {
  2. public static void main(String[] args) {
  3. /**
  4. * 核心线程数量为1 , 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s
  5. */
  6. ThreadPoolExecutor threadPoolExecutor;
  7. threadPoolExecutor = new ThreadPoolExecutor(1 , 3 , 20 , TimeUnit.SECONDS ,
  8. new ArrayBlockingQueue<>(1) , Executors.defaultThreadFactory() , new ThreadPoolExecutor.CallerRunsPolicy());
  9. // 提交5个任务
  10. for(int x = 0 ; x < 5 ; x++) {
  11. threadPoolExecutor.submit(() -> {
  12. System.out.println(Thread.currentThread().getName() + "---->> 执行了任务");
  13. });
  14. }
  15. }
  16. }

0ae30edf7d854bdd95bb96a64a64aa65.png

发表评论

表情:
评论列表 (有 0 条评论,138人围观)

还没有评论,来说两句吧...

相关阅读

    相关 java线线

    java多线程之线程池 池化技术 程序的运行,其本质上,是对系统资源(CPU、内存、磁盘、网络等等)的使用。如何高效的使用这些资源是我们编程优化演进的一个方向。今天说

    相关 线线

    前言: 1. 系统启动一个新线程的成本是比较高的,因为它涉及与操作系统交互。在这种情形下,使用线程池可以很好地提高性能,尤其是当程序中需要创建大量生存期很短暂的线程时,