Java SE 拓展篇 P3 使用线程池代替显式创建线程

1 线程池的特点

1.1 显式创建线程的缺点

在这里插入图片描述
在先前的学习中,对于多线程编程,都是在某个类中实现Runnable接口并实现run()方法,接着使用

new Thread(this).start();

来直接创建线程并执行,但这种方法受到了来自插件的警告,不要显式创建线程,使用线程池代替

在了解线程池前,先来看显式创建线程的缺点:
线程是资源的一种,显式地创建线程并销毁的过程必然要频繁使用内存,而内存是极为宝贵的资源,为此就需要使用线程池来替代

1.2 线程池及其优点

顾名思义,即管理存储线程的空间,使用线程池可以方便地管理线程,并减少对内存地消耗

使用线程池的优点:

1,减少内存资源的消耗
即通过重复使用已经创建的线程降低线程创建和消耗带来的损耗
2,提高请求的处理速度
当任务到达时,任务不需要等待线程创建完毕才能执行
3,提高线程的可管理性
线程是稀缺资源,不能无限制创建线程,使用线程池可以统一地分配,监控,优化

1.3 线程池的工作原理

在这里插入图片描述

2 线程池的实现方式

2.1 ThreadPoolExecutor类

线程池的真正实现方式即ThreadPoolExector类
它有四种构造方法,最主要的是:

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;

该构造方法创建一个ThreadPoolExecutor对象需要7个参数:

1: int corePoolSize
核心线程数(必要参数)
默认情况下,核心线程会一直存活
2: int maximumPoolSize
线程池所能容纳的最大线程数(必要参数)
超过该值后后续的新任务会被阻塞
3: long keepAliveTime
非核心线程存在时长(必要参数)
如果超过该值,非核心线程会被回收
4: TimeUnit unit
指定keepAliveTime的参数时间单位(必要参数)
可以是毫秒,秒,分
如TimeUnit.SECONDS
5: BlockingQueue<Runnable> workQueue
阻塞式任务队列(必要参数)
将Runnable的对象存储在队列中,如new ArrayBlockingQueue<Runnable>(3)
6: ThreadFactory threadFactory
线程工厂(可选参数)
指定为线程池创建新线程的方式
7: RejectedExecutionHandler handler
拒绝策略(可选参数)
当达到最大线程数时需要执行的策略
如:
handler的拒绝策略:
AbortPolicy:不执行新任务,直接抛出异常,提示线程池已满
DisCardPolicy:不执行新任务,也不抛出异常
DisCardOldSetPolicy:将消息队列中的第一个任务替换为当前新进来的任务执行             
CallerRunsPolicy:直接调用execute来执行当前任务

2.2 创建线程池及关于线程池的方法

2.2.1 使用ThreadPoolExecutor类创建一个线程池

**
 * @author* @date 2021/1/26 - 9:18
 * @function 线程池创建测试
 */
public class ThreadPoolExecutorTest {
    
    

    public static void main(String[] args) {
    
    
        
        ThreadPoolExecutor threadPool =
                new ThreadPoolExecutor(5, 6, 60,
                        TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(3));

       Runnable task1 = new Runnable() {
    
    
           @Override
           public void run() {
    
    
               for(int i = 1; i <= 10; i++) {
    
    
                   System.out.println("任务1被" + i + "次执行");
               }
           }
       };
       threadPool.execute(task1);

        Runnable task2 = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i = 1; i <= 10; i++) {
    
    
                    System.out.println("任务2被" + i + "次执行");
                }
            }
        };
        threadPool.execute(task2);

        Runnable task3 = new Runnable() {
    
    
            @Override
            public void run() {
    
    
                for(int i = 1; i <= 10; i++) {
    
    
                    System.out.println("任务3被" + i + "次执行");
                }
            }
        };
        threadPool.execute(task3);

        threadPool.shutdown();
    }
    
}

这里没有使用ThreadFactory和RejectedExecutionHandler参数,演示效果:
在这里插入图片描述

2.2.2 关于线程池的方法

//向线程池提交一个任务
threadPool.execute(task);

//设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdown();

// 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
threadPool.shutdownNow(); 

2.3 基于ThreadPoolExecutor几种线程池

CachedThreadPool:可缓存的线程池
该线程池中没有核心线程,非核心线程的数量为Integer.max_value,就是无限大
当有需要时创建线程来执行任务,没有需要时回收线程
适用于耗时少,任务量大的情况

SecudleThreadPool:周期性执行任务的线程池
按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程
非核心线程的大小也为无限大
适用于执行周期性的任务。

SingleThreadPool:只有一条线程来执行任务
适用于有顺序的任务的应用场景。

FixedThreadPool:定长的线程池
有核心线程,核心线程的即为最大的线程数量,没有非核心线程

2.4 小结

在这里插入图片描述

在使用ThreadPoolExecutor类创建线程池时,插件给出了警告,应该给出创建线程方式的ThreadFactory参数

对于创建线程池有5种方式:

1,ThreadPoolExecutor
2,FixedThreadPool
3,SecudleThreadPool
4,SingleThreadPool
5,CachedThreadPool

最好使用ThreadPoolExecutor类来创建线程池,它的参数列表能够很清晰地看出该线程池的属性

猜你喜欢

转载自blog.csdn.net/weixin_43541094/article/details/113174051
今日推荐