Java高并发编程中Executors静态工厂的使用及详细介绍-刘宇

作者:刘宇
CSDN博客地址:https://blog.csdn.net/liuyu973971883
有部分资料参考,如有侵权,请联系删除。如有不正确的地方,烦请指正,谢谢。

一、Executors静态工厂的作用

我们不可能通过创建ThreadPoolExecutor的实例来配置线程池,而Java中为我们提供了更加便捷的方法,就是Executors静态工厂。严格意义上讲Executors并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。下面我们就逐个讲解Executors中创建不同线程池的方法。

二、newCachedThreadPool

1、newCachedThreadPool的特点

创建一个可缓存线程池,线程池初始的线程数为0,使用SynchronousQueue队列存储任务,这个队列只能存储一个任务,该线程池的最大线程数为Integer的最大值。因为队列中只能存储一个任务,所以当队列处于饱和状态时,该线程池就会创建新的线程。空闲的线程会在60秒后关闭,因为线程的coreThread为0,当空闲线程销毁为0的时候该线程池就会关闭。大概是如下几个特点:

  • 初始化线程大小为0
  • 使用的是SynchronousQueue队列,只能存储一个任务
  • 最大线程数为Integer的最大值
  • 60秒后将空闲线程关闭后,当线程池内线程为0时会自动关闭线程池

2、适用场景

  • 只适合短暂的任务,如果是非常耗时的任务不建议使用,因为每次提交一个任务都会开启一个线程
  • 用来创建一个可以无限扩大的线程池,适用于服务器负载较轻,执行很多短期异步任务。

3、演示案例

package com.brycen.part3.threadpool;

import java.util.concurrent.*;
import java.util.stream.IntStream;

public class ExecutorsExample {

    public static void main(String[] args) throws InterruptedException {
        useCachedThreadPool();
    }

    private static void useCachedThreadPool() throws InterruptedException {
        ExecutorService executorService = Executors.newCachedThreadPool();
        //查看线程池中的线程数
        System.out.println("poolSize: "+((ThreadPoolExecutor)executorService).getPoolSize() );
        IntStream.rangeClosed(1,10).forEach(i->executorService.execute(()->{
            try {
                //休眠10秒
                TimeUnit.SECONDS.sleep(10);
                System.out.println(Thread.currentThread().getName()+" ["+i+"]");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
        //休眠100毫秒,确保任务全部提交进去
        TimeUnit.MILLISECONDS.sleep(100);
        //查看线程池中的线程数
        System.out.println("poolSize: "+((ThreadPoolExecutor)executorService).getPoolSize() );
    }
}

运行结果:

  • 60秒后当线程减为0时,线程池关闭,程序关闭。
poolSize: 0
poolSize: 10
pool-1-thread-1 [1]
pool-1-thread-7 [7]
pool-1-thread-4 [4]
pool-1-thread-6 [6]
pool-1-thread-5 [5]
pool-1-thread-3 [3]
pool-1-thread-2 [2]
pool-1-thread-8 [8]
pool-1-thread-9 [9]
pool-1-thread-10 [10]

Process finished with exit code 0

三、newFixedThreadPool

1、newFixedThreadPool的特点

该线程池的线程数在创建时就被固定了,不可以扩大。线程池中的每个线程都是处于活动状态,该线程池会一直存在,直到调用shutdown。该线程池试用的是LinkedBlockingQueue队列,最大任务数为Integer的最大值。该线程池的空闲时间为0,因为不可以扩大线程数,所以也就不存在回收线程。大概是如下几个特点:

  • 初始化时该线程池中的线程数就定了,是固定的,不可以扩大。
  • 使用的是LinkedBlockingQueue队列,最大数为Integer最大值
  • 该线程池的空闲线程空闲时间为0,因为不可以扩大线程数,所以也就不存在回收线程
  • 当线程处于空闲,且任务队列为空时,线程池也不会关闭

2、适用场景

主要适用于固定大小的线程池,因为它是无界的阻塞队列,那么线程池中的线程不会扩大,适用与可以预测线程数的场景中,或者服务器的负载很高,需要对线程数量进行严格控制的场景中。

3、演示案例

package com.brycen.part3.threadpool;

import java.util.concurrent.*;
import java.util.stream.IntStream;

public class ExecutorsExample {

    public static void main(String[] args) throws InterruptedException {
        useFixedThreadPool();
    }

    private static void useFixedThreadPool() throws InterruptedException {
        ExecutorService executorService = Executors.newFixedThreadPool(5);
        //查看线程池中的线程数,默认情况下,只有当线程池接受到任务时才开始创建线程,所以这边输出0
        System.out.println("poolSize: "+((ThreadPoolExecutor)executorService).getPoolSize() );
        IntStream.rangeClosed(1,10).forEach(i->executorService.execute(()->{
            try {
                //休眠10秒
                TimeUnit.SECONDS.sleep(10);
                System.out.println(Thread.currentThread().getName()+" ["+i+"]");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
        //休眠100毫秒,确保任务全部提交进去
        TimeUnit.MILLISECONDS.sleep(100);
        //查看线程池中的线程数
        System.out.println("poolSize: "+((ThreadPoolExecutor)executorService).getPoolSize() );
    }
}

运行结果:

  • 只能有5个线程执行任务,后续的任务只能等待前面的任务执行完,该线程池不会自动关闭。
poolSize: 0
poolSize: 5
pool-1-thread-5 [5]
pool-1-thread-2 [2]
pool-1-thread-3 [3]
pool-1-thread-4 [4]
pool-1-thread-1 [1]
pool-1-thread-2 [7]
pool-1-thread-4 [9]
pool-1-thread-3 [8]
pool-1-thread-5 [6]
pool-1-thread-1 [10]

四、newSingleThreadExecutor

1、newSingleThreadExecutor的特点

该线程池只会有一个线程处于活动状态,所以任务只能一个一个被执行。其实内部还是使用的newFixedThreadPool,只不过现在了只有一个线程,与newFixedThreadPool(1)不同是不能重新配置加入线程,因为它返回的是一个代理的包装过的ExecutorService。该线程池能够保证任务的执行顺序,先提交的先执行。如果在执行期间线程出现异常,那么会创建一个新的线程替换。SingleThreadExecutor与单独new出来的线程区别在于new出来的Thread在任务结束之后也就会销毁,而且也不可以submit提交任务到队列。总结有如下几点:

  • 其实内部还是使用的FixedThreadPool,只不过限制了只有1个线程而已
  • 该线程池里的任务只能被一个一个执行
  • newSingleThreadExecutor返回的是一个经过代理的ExecutorService,不能转换为ThreadPoolExecutor,这也就意味着它只有一些ExecutorService的基本方法
  • 能够保证任务的执行顺序
  • 当线程出现异常时会重新创建一个线程替换
  • SingleThreadExecutor与单独new出来的Thread区别在于,单独new出来的Thread任务结束之后线程也就会随着结束,而且不可以submit提交任务到队列

2、适用场景

适用于需要保证任务被按顺序执行,并且在任何时候都不会出现多个线程的情况。

3、演示案例

package com.brycen.part3.threadpool;

import java.util.concurrent.*;
import java.util.stream.IntStream;

public class ExecutorsExample {

    public static void main(String[] args) throws InterruptedException {
        useSingleThreadPool();
    }

    private static void useSingleThreadPool() throws InterruptedException {
        ExecutorService executorService = Executors.newSingleThreadExecutor();

        //因为不能转换为ThreadPoolExecutor,所以也就没有getPoolSize等一些方法
		//System.out.println("poolSize: "+((ThreadPoolExecutor)executorService).getPoolSize() );
        IntStream.rangeClosed(1,10).forEach(i->executorService.execute(()->{
            try {
                //休眠10秒
                TimeUnit.SECONDS.sleep(10);
                System.out.println(Thread.currentThread().getName()+" ["+i+"]");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }));
    }
}

运行结果:

  • 按顺序一个一个执行,并且在执行所有任务后该线程池不会自动关闭。
pool-1-thread-1 [1]
pool-1-thread-1 [2]
pool-1-thread-1 [3]
pool-1-thread-1 [4]
pool-1-thread-1 [5]
pool-1-thread-1 [6]
pool-1-thread-1 [7]
pool-1-thread-1 [8]
pool-1-thread-1 [9]
pool-1-thread-1 [10]

五、newWorkStealingPool

1、newWorkStealingPool的特点

  • 该方法会根据你的CPU核数来创建线程个数,也可指定线程数
  • newWorkStealingPool其内部使用的是ForkJoinPool,在任务全部执行完之后该线程池会自动关闭

2、适用场景

创建一个拥有多个任务队列的线程池,可以减少连接数,创建当前可用cpu数量的线程来并行执行,适用于大耗时的操作,可以并行来执行

3、演示案例

package com.brycen.part3.threadpool;

import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

public class ExecutorsExample2 {

    public static void main(String[] args) throws InterruptedException {
        //我的CPU核数是4,所以创建了4个线程
        ExecutorService executorService = Executors.newWorkStealingPool();

        //生成10个callable任务
        List<Callable<String>> callableList = IntStream.rangeClosed(1, 10).boxed().map(i -> (Callable<String>) () -> {
            System.out.println("Thread " + Thread.currentThread().getName());
            TimeUnit.SECONDS.sleep(2);
            return "Task" + i;
        }).collect(Collectors.toList());

        //执行list集合中所有的callable并进行阻塞
        List<Future<String>> futureList = executorService.invokeAll(callableList);
        //获取callable执行完的返回值
        for (Future<String> future:futureList) {
            try {
                //进行阻塞
                String result = future.get();
                System.out.println(result);
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
        }
    }
}

运行结果:

  • 执行完成后会自动关闭线程池
Thread ForkJoinPool-1-worker-1
Thread ForkJoinPool-1-worker-2
Thread ForkJoinPool-1-worker-3
Thread ForkJoinPool-1-worker-0
Thread ForkJoinPool-1-worker-1
Thread ForkJoinPool-1-worker-0
Thread ForkJoinPool-1-worker-2
Thread ForkJoinPool-1-worker-3
Thread ForkJoinPool-1-worker-3
Thread ForkJoinPool-1-worker-2
Task1
Task2
Task3
Task4
Task5
Task6
Task7
Task8
Task9
Task10

Process finished with exit code 0
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值