java线程池的使用

线程池的使用

标签(空格分隔): 多线程


Executors工具类(创建池与提交任务)

通过工具类,可以创建线程池
常见的几种线程池创建

通过Executors的静态方法创建线程池

1.创建固定大小的线程池(可用于服务器瞬时削峰、限流,但需注意长时间持续高峰情况造成的队列阻塞)

ExecutorService executorService = Executors.newFixedThreadPool(int n);//n是线程池大小

2.创建可变大小的线程池(适用场景:快速处理大量耗时较短的任务)

ExecutorService executorService = Executors.newCachedThreadPool()

3.创建单线程的线程池(因为只有一个线程,所以提交的任务都是串行执行,可以解决并发执行带来的同步问题)

ExecutorService executorService = Executors.newSingleThreadExecutor()
ps:这个线程池,只会创建一次线程,用这个线程来执行所有提交过来的任务,并不是每来一次任务创建一次

4.创建一个可定期或者延时执行任务的定长线程池,支持定时及周期性任务执行

ScheduledExecutorService executors.newScheduledThreadPool()

延迟3秒钟后执行任务
        scheduledThreadPool.schedule(new Runnable() {
        @Override
        public void run() {
            System.out.println("运行时间: " + sdf.format(new Date()));
        }
    }, 3, TimeUnit.SECONDS);
延迟1秒钟后每隔3秒执行一次任务
  scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
        @Override
        public void run() {
            System.out.println("运行时间: " + sdf.format(new Date()));
        }
    }, 1, 3, TimeUnit.SECONDS);

5.创建一个具有抢占式操作的线程池(JDK1.8新增,适合使用在很耗时的操作)

newWorkStealingPool适合使用在很耗时的操作
ExecutorService executorService = Executors.newWorkStealingPool();
WorkStealingPool
工作窃取线程池
假设共有三个线程同时执行, A, B, C
当A,B线程池尚未处理任务结束,而C已经处理完毕,则C线程会从A或者B中窃取任务执行,这就叫工作窃取
假如A线程中的队列里面分配了5个任务,而B线程的队列中分配了1个任务,当B线程执行完任务后,它会主动的去A线程中窃取其他的任务进行执行WorkStealingPool 背后是使用 ForkJoinPool实现的

public class T11_WorkStealingPool {

public static void main(String[] args) throws IOException {
    // CPU 核数
    System.out.println(Runtime.getRuntime().availableProcessors());
    
    // workStealingPool 会自动启动cpu核数个线程去执行任务
    ExecutorService service = Executors.newWorkStealingPool();
    service.execute(new R(10000));  // 我的cpu核数为12 启动13个线程,其中第一个是1s执行完毕,其余都是2s执行完毕,
                                            // 有一个任务会进行等待,当第一个执行完毕后,会再次偷取第十三个任务执行
    for (int i = 0; i < Runtime.getRuntime().availableProcessors(); i++) {
        service.execute(new R(2000));
    }
    
    // 因为work stealing 是deamon线程,即后台线程,精灵线程,守护线程
    // 所以当main方法结束时, 此方法虽然还在后台运行,但是无输出
    // 可以通过对主线程阻塞解决
    System.in.read();
}

static class R implements Runnable {

    int time;

    R(int time) {
        this.time = time;
    }

    @Override
    public void run() {
        try {
            TimeUnit.MILLISECONDS.sleep(time);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "  " + time);
    }
}

}

通过ExecutorService实例提交任务

a.通过sumbit提交任务,带Future返回值,任务类型可以为Runable和Callable型

1. 当任务类型为Runnable型的时候

Future future = executorService.submit(runnable) 

执行执行完毕后,返回null,future.get(),将阻塞主线程,获取null值
2. 当任务类型为Callable型的时候

Future future = executorService.submit(callable)

任务执行完毕后,返回执行结果,执行结果类型与Callable泛型一致,future.get()阻塞主线程,并获取返回值

b.通过execute提交任务,没有返回值,任务类型只能是Runable类型
executorService. (runnable) 

ThreadPoolExecutor创建方式

ThreadPoolExecutor提供了四个构造方法,提供不同的参数类型

1	corePoolSize	int	核心线程池大小
2	maximumPoolSize	int	最大线程池大小
3	keepAliveTime	long	线程最大空闲时间
4	unit	TimeUnit	时间单位
5	workQueue	BlockingQueue<Runnable>	线程等待队列
6	threadFactory	ThreadFactory	线程创建工厂
7	handler	RejectedExecutionHandler	拒绝策略。

线程池的关闭

在大多数人开发过程中,大家关注的是如何创建、使用线程池,但很少有人去关系线程池的关闭,甚至忘记调用shutdown()方法,这可能导致内存溢出。
注意:FixedThreadPool的核心线程不会自动超时关闭,必须要手动的调用shutdown关闭线程池,不然会导致内存溢出。
线程池自动关闭的两个条件:
1.线程池的引用不可达
2.线程池中没有线程;

两个关闭方法差异
1.shutdown()
线程池虽然关闭,但是队列中已有的任务仍然会继续执行
2.shutdownNow()
强制关闭线程池,线程的任务队列里的线程将不会被执行,而正在被执行的线程也会中断(可以在interrupted中处理)

正确的关闭线程池,需要考虑以下3个问题
1.如何拒绝新来的请求任务
2.如何处理线程池等待队列里的任务
3.如何处理正在执行的任务

线程池的拒绝策略

默认的拒绝策略
ThreadPoolExecutor源码中可以看到以下内容
private static final RejectedExecutionHandler defaultHandler = new AbortPolicy();
线程池的默认拒绝策略为AbortPolicy,即丢弃任务并抛出RejectedExecutionException异常

1.AbortPolicy(默认,丢弃并抛异常)

ThreadPoolExecutor.AbortPolicy
丢弃任务并抛出RejectedExecutionException异常。
这是线程池默认的拒绝策略,在任务不能再提交的时候,抛出异常,及时反馈程序运行状态。如果是比较关键的业务,推荐使用此拒绝策略,这样子在系统不能承载更大的并发量的时候,能够及时的通过异常发现。

2.DiscardPolicy(静默丢弃,不会给出任何异常提示)

ThreadPoolExecutor.DiscardPolicy
建议是一些无关紧要的业务采用此策略

3.DiscardOldestPolicy(喜新厌旧的拒绝策略)

ThreadPoolExecutor.DiscardOldestPolicy
它会丢掉队列最前面的任务,然后接收新来的任务,典型的是一种喜新厌旧的策略方式

4.CallerRunsPolicy(我干不了,你自己干吧)

ThreadPoolExecutor.CallerRunsPolicy由调用线程处理该任务
你提交给我,但是我没时间干呀,你自己干吧。

posted @ 2021-03-08 18:19  心若向阳花自开  阅读(599)  评论(0编辑  收藏  举报