Java线程池总结--java几种线程池

目录

  • 四种线程池介绍:
  • 各个线程池代码实例
  • 重点
    • 1、底层结构实现原理:是基于阻塞队列实现
    • 2、自己构建一个线程池参数有哪些
    • 3、底层实现原理:
    • 4、拒绝策略:
    • 5、线程存活问题
  • ThreadPoolExecutor构造方法

四种线程池介绍:

  • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
  • newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

各个线程池代码实例

(1).newCachedThreadPool 创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程
线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程

//为了体验到复用线程效果,每一个线程进行100毫秒*n休眠
public static void main(String[] args) {
    ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
    for (int i = 0; i < 5; i++) {
        final int index = i;
        try {
            Thread.sleep(index * 100);
        } catch (Exception e) {
            e.printStackTrace();
        }
        cachedThreadPool.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(index+" 当前线程"+Thread.currentThread().getName());
            }
        });
    }
}

结果如下:

0 当前线程pool-1-thread-1
1 当前线程pool-1-thread-1
2 当前线程pool-1-thread-1
3 当前线程pool-1-thread-1
4 当前线程pool-1-thread-1

(2). newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

//线程池中设置数量为3个,那么只会有三个线程执行10条数据
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
    final int index = i;
    fixedThreadPool.execute(new Runnable() {
    	@Override
        public void run() {
            try {
                Thread.sleep(2000);
                System.out.println(index+" 当前线程"+Thread.currentThread().getName());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}

结果如下:

0 当前线程pool-1-thread-1
1 当前线程pool-1-thread-2
2 当前线程pool-1-thread-3
3 当前线程pool-1-thread-1
4 当前线程pool-1-thread-2
5 当前线程pool-1-thread-3
6 当前线程pool-1-thread-1
7 当前线程pool-1-thread-2
8 当前线程pool-1-thread-3
9 当前线程pool-1-thread-1

(3).newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行
线程池延迟3秒后执行

ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.schedule(new Runnable() {
    @Override
    public void run() {
        System.out.println("延迟3秒后执行");
    }
}, 3, TimeUnit.SECONDS);

(4).newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
下面是按照顺序执行FIFO(一般情况下多线程index是乱的,该线程池保证了按照顺序执行)

ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
    final int index = i;
    singleThreadExecutor.execute(new Runnable() {
        @Override
        public void run() {
            try {
                System.out.println("--index--"+index);
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    });
}

结果如下:

--index--0
--index--1
--index--2
--index--3
--index--4
--index--5
--index--6
--index--7
--index--8
--index--9

重点

1、底层结构实现原理:是基于阻塞队列实现

  • newCachedThreadPool 默认是用SynchronousQueue队列实现
  • newFixedThreadPool 默认是用LinkedBlockingQueue队列实现
  • newScheduledThreadPool默认是用DelayedWorkQueue队列实现
  • newSingleThreadExecutor默认是用LinkedBlockingQueue队列实现

2、自己构建一个线程池参数有哪些

public ThreadPoolExecutor(int corePoolSize, 
						  int maximumPoolSize, 
						  long keepAliveTime,   
						  TimeUnit unit, 
						  BlockingQueue<Runnable> workQueue );

1、corePoolSize: 核心线程数
2、maximumPoolSize:最大支持线程数
3、keepAliveTime :当线程空闲超过一段时间,线程池会判断:运行的线程数大于 corePoolSize,线程被停掉
4、TimeUnit :过期时间单位
5、workQueue:使用什么队列

例如:

//举例如下:
public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, 
        							  Integer.MAX_VALUE,
                                      60L, 
                                      TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>() );
}

3、底层实现原理:

首先调用executor()执行任务,进入线程池,此时线程数还未超过corePoolSize,当线程数超过corePoolSize时,线程会进入阻塞队列等待,当阻塞队列的线程存到一定数量时,线程池会扩容,但不会超过最大线程数,maximumPoolSize,当线程数量超过最大线程数时,线程池会启动拒绝策略,来保证任务正常进行,当任务线程数越来越少时,会逐渐恢复到corePoolSize

4、拒绝策略:

Java线程池总结--java几种线程池_第1张图片
常用的四种拒绝策略:

  • AbortPolicy 直接抛出RejectedExecutionExeception异常来阻止系统正常运行 默认策略
  • CallerRunsPolicy 将任务回退到调用者
  • DisOldestPolicy 丢掉等待最久的任务‘
  • DisCardPolicy 直接丢弃任务

这四种拒绝策略均实现的RejectedExecutionHandler接口

线程池默认的拒绝行为是AbortPolicy,也就是抛出RejectedExecutionHandler异常,该异常是非受检异常,很容易忘记捕获。如果不关心任务被拒绝的事件,可以将拒绝策略设置成DiscardPolicy,这样多余的任务会悄悄的被忽略。

自定义拒绝策略
实现 RejectedExecutionHandler 接口来实现自己的拒绝策略,如代码所示:
Java线程池总结--java几种线程池_第2张图片

5、线程存活问题

newCachedThreadPool 默认启用一个线程。默认存活时间60s,如果超过60s第一个线程销毁,重新创建一个新的线程
newSingleThreadExecutor:默认启用一个线程。不过期,自始至终只有一个线程来执行任务
newFixedThreadPool :设置固定核心线程数,有多个核心线程执行任务
newScheduledThreadPool:设置固定核心线程数,有多个核心线程执行任务

ThreadPoolExecutor构造方法

Java中创建线程池很简单,只需要调用 Executors 中相应的便捷方法即可,比如:Executors.newFixedThreadPool(int nThreads),但是便捷不仅隐藏了复杂性,也为我们埋下了潜在的隐患(OOM,线程耗尽)。

小程序使用这些快捷方法没什么问题,对于服务端需要长期运行的程序,创建线程池应该直接使用 ThreadPoolExecutor 的构造方法。

Executors中创建线程池的快捷方法,实际上是调用了ThreadPoolExecutor的构造方法(定时任务使用的是ScheduledThreadPoolExecutor),该类构造方法参数列表如下:

// Java线程池的完整构造函数
public ThreadPoolExecutor(
  int corePoolSize, // 线程池长期维持的线程数,即使线程处于Idle状态,也不会回收。
  int maximumPoolSize, // 线程数的上限
  long keepAliveTime, TimeUnit unit, // 超过corePoolSize的线程的idle时长,
                                     // 超过这个时间,多余的线程会被回收。
  BlockingQueue workQueue, // 任务的排队队列
  ThreadFactory threadFactory, // 新线程的产生方式
  RejectedExecutionHandler handler) // 拒绝策略

这些参数中,比较容易引起问题的有corePoolSize, maximumPoolSize, workQueue以及handler:

  • corePoolSize和maximumPoolSize 设置不当会影响效率,甚至耗尽线程;
  • workQueue 设置不当容易导致OOM;
  • handler 设置不当会导致提交任务时抛出异常。

你可能感兴趣的:(java,java,多线程)