JAVA 多线程-newSingleThreadExecutor()

3,269 阅读3分钟
  • 常用多线程
//创建单线程的线程池
ExecutorService executor01 = Executors.newSingleThreadExecutor();
  • 使用方式及参数配置详解
  1. Executors.newSingleThreadExecutor()
/**
  * 创建一个Executor,使用一个工作线程操作
  * 关闭一个无界队列。
  * (但是请注意,如果这个线程在之前的执行期间由于失败而终止关闭后,如果需要执行后续任务,将会有一个新的 
  * 文件取代它的位置。(则会创建一个新的线程来代替他))
  * 任务被保证执行按顺序,任何时刻都不超过一个任务处于活动状态给定的时间。与其他等价的
  * {@code newFixedThreadPool(1)}不同,返回的执行器是保证不能重新配置使用额外的线程。
  * @返回新创建的单线程Executor
  * /
public static ExecutorService newSingleThreadExecutor() {
    return new FinalizableDelegatedExecutorService
        (new ThreadPoolExecutor(1, 1,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>()));
}
public ThreadPoolExecutor(int corePoolSize,     // 核心线程数
                          int maximumPoolSize,  // 最大线程数
                          long keepAliveTime,   // 保活时间
                          TimeUnit unit,        // 保活时间单位
                          BlockingQueue<Runnable> workQueue) {  // 存放Runnable实现的队列
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

在SingleThreadExecutor实现中:

corePoolSize && maximumPoolSize,corePoolSize 和 maximumPoolSize 都被设置为:1。

keepAliveTime => keepAliveTime为0,意味着多余的空闲线程会被立即终止。

workQueue => 采用无界队列 LinkedBlockingQueue 作为线程池的工作队列(队列的容量为Integer.MAX_VALUE),会在循环中反复从LinkedBlockingQueue获取任务来执行。
  • 疑问既然 new Thread()和 newSingleThreadExecutor() 都是创建一个线程处理,为什么还需要存在单个线程的线程池呢?
  1. 案例演示
public static void main(String[] args) {
    for (int i = 1; i <=10; i++) {
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName()+" 办理业务");
        }).start();
    }
}

返回结果:且每次都是新建一个线程去处理
Thread-1 办理业务
Thread-2 办理业务
Thread-3 办理业务
Thread-5 办理业务
Thread-0 办理业务
Thread-4 办理业务
Thread-6 办理业务
Thread-8 办理业务
Thread-9 办理业务
Thread-7 办理业务
public static void main(String[] args) {

    //一池一线程
    ExecutorService threadPool = Executors.newSingleThreadExecutor();

    //10个顾客请求
    try {
        for (int i = 1; i <=10; i++) {
            //执行
            threadPool.execute(()->{
                System.out.println(Thread.currentThread().getName()+" 办理业务");
            });
        }
    }catch (Exception e) {
        e.printStackTrace();
    }finally {
        //关闭
        threadPool.shutdown();
    }

}

返回结果:且每次都是同一个线程去处理
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务
pool-1-thread-1 办理业务

  • 使用场景
1. 单线程化线程池 SingleThreadExecutor 特点:

-    只有一个核心线程(保证所有任务按照指定顺序在一个线程中执行,不需要处理线程同步的问题)。

-    应用场景:不适合并发但可能引起IO阻塞性及影响UI线程响应的操作,如数据库操作,文件操作等。

-    任务需要按照顺序执行 【串行处理任务】

2. SingleThreadExecutor 的意义:

-    缓存线程、进行池化,可实现线程重复利用、避免重复创建和销毁所带来的性能开销。

-    当线程调度任务出现异常时,会重新创建一个线程替代掉发生异常的线程。

-    任务执行按照规定的调度规则执行。线程池通过队列形式来接收任务。再通过空闲线程来逐一取出进行任务调度。即线程池可以控制任务调度的执行顺序。

-    可制定拒绝策略。即任务队列已满时,后来任务的拒绝处理规则。

- 总结

```js

new Thead()的方式每次都会新建一个线程来处理,增加消耗;

newSingleThreadExecutor()的方式只使用同一个线程来处理,减少了消耗;

只含有单个线程的线程池 重点在于有序的执行任务 多个任务根据提交线程池的先后 FIFO 执行