Java多线程-线程池的使用

线程池的优势

  • 线程频繁的建立=>销毁=>建立对系统对开销很大,使用线程池能够避免重复的开销
  • 方便复用,提升相应速度
  • 线程的建立于执行彻底分开,方便维护,下降耦合

线程池的实现原理

池化技术

一说到线程池天然就会想到池化技术java

其实所谓池化技术,就是把一些可以复用的东西放到池中,避免重复建立、销毁的开销,从而极大提升性能。并发

常见池化技术的例如:性能

  • 线程池
  • 内存池
  • 链接池

Java中的实现

官方接口

JDK 1.5 推出了三大API用来建立线程:线程

  • Executors.newCachedThreadPool():无限线程池(最大21亿)
  • Executors.newFixedThreadPool(nThreads):固定大小的线程池
  • Executors.newSingleThreadExecutor():单个线程的线程池

这三个API的底层其实都是由同一个类实现的:ThreadPoolExecutorcode

public static ExecutorService newCachedThreadPool() {
  return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                60L, TimeUnit.SECONDS,
                                new SynchronousQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,
                                0L, TimeUnit.MILLISECONDS,
                                new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newSingleThreadExecutor() {
  return new FinalizableDelegatedExecutorService
    (new ThreadPoolExecutor(1, 1,
                            0L, TimeUnit.MILLISECONDS,
                            new LinkedBlockingQueue<Runnable>()));
}

ThreadPoolExecutor

七大参数

ThreadPoolExecutor类主要有如下七个参数:对象

  • int corePoolSize: 核心线程池大小
  • int maximumPoolSize: 最大核心线程池大小
  • long keepAliveTime: 线程空闲后的存活时间
  • TimeUnit unit: 超时单位
  • BlockingQueue<Runnable> workQueue: 阻塞队列
  • ThreadFactory threadFactory: 线程工厂:建立线程的,通常默认
  • RejectedExecutionHandler handle: 拒绝策略

四种拒绝策略

拒绝策略就是当队列满时,线程如何去处理新来的任务。继承

Java内置了四种拒绝策略:接口

  • ThreadPoolExecutor.CallerRunsPolicy()队列

  • ThreadPoolExecutor.AbortPolicy()内存

  • ThreadPoolExecutor.DiscardPolicy()

  • ThreadPoolExecutor.DiscardOldestPolicy()

CallerRunsPolicy(调用者运行策略)

功能:只要线程池没有关闭,就由提交任务的当前线程处理。

使用场景:通常在不容许失败、对性能要求不高、并发量较小的场景下使用。

AbortPolicy(停止策略)

功能:当触发拒绝策略时,直接抛出拒绝执行的异常

使用场景:ThreadPoolExecutor中默认的策略就是AbortPolicy,因为ExecutorService接口的系列ThreadPoolExecutor都没有显示的设置拒绝策略,因此默认的都是这个。

DiscardPolicy(丢弃策略)

功能:直接丢弃这个任务,不触发任何动做

使用场景:提交的任务可有可无,通常用的少。

DiscardOldestPolicy(弃老策略)

功能:弹出队列头部的元素,而后尝试执行,至关于排队的时候把第一我的打死,而后本身代替

使用场景:发布消息、修改消息相似场景。当老消息还未执行,此时新的消息又来了,这时未执行的消息的版本比如今提交的消息版本要低就能够被丢弃了。

线程池中的状态

img

  • RUNNING 天然是运行状态,指能够接受任务执行队列里的任务
  • SHUTDOWN 指调用了 shutdown() 方法,再也不接受新任务了,可是队列里的任务得执行完毕。
  • STOP 指调用了 shutdownNow() 方法,再也不接受新任务,同时抛弃阻塞队列里的全部任务并中断全部正在执行任务。
  • TIDYING 全部任务都执行完毕,在调用 shutdown()/shutdownNow() 中都会尝试更新为这个状态。
  • TERMINATED 终止状态,当执行 terminated() 后会更新为这个状态。

处理流程

提交一个任务到线程池中,线程池的处理流程以下:

  • 判断线程池里的核心线程是否都在执行任务

    • 是:进入下个流程。
    • 否:调用/建立一个新的核心线程来执行任务。
  • 线程池判断工做队列是否已满

    • 是:进入下个流程。
    • 否:将新提交的任务存储在这个工做队列里。
  • 判断线程池里的全部线程是否都处于工做状态

    • 是:交给拒绝策略来处理这个任务。
    • 否:调用/建立一个新的工做线程来执行任务。

具体使用

建立

不过最好不要使用Executors来建立线程,缘由以下(参考自——阿里巴巴Java开发手册):

  • FixedThreadPoolSingleThreadPool: 容许的请求队列长度为 Integer.MAX_VALUE,可能会堆积大量的请求,从而致使 OOM
  • CachedThreadPool: 容许的建立线程数量为 Integer.MAX_VALUE,可能会建立大量的线程,从而致使 OOM

推荐使用ThreadPoolExecutor类自行建立

// 自定义线程池
ExecutorService threadPool = new ThreadPoolExecutor(
  2,
  Runtime.getRuntime().availableProcessors(),//CPU的核心数,适合CPU密集型任务
  3,
  TimeUnit.SECONDS,
  new LinkedBlockingDeque<>(3),
  Executors.defaultThreadFactory(),
  new ThreadPoolExecutor.DiscardOldestPolicy());

合理配置线程

线程池不是越大越好,要根据任务类型合理进行配置

  • IO 密集型任务:尽量的多配置线程
  • CPU 密集型任务:(大量复杂的运算)应当分配较少的线程

执行

有两个方法能够执行任务executesubmit

  • execute提交没有返回值,不能判断是否执行成功。
  • submit会返回一个Future对象,经过Futureget()方法来获取返回值。

区别:

  • 接收的参数不同
    • execute提交的方式只能提交一个Runnable的对象
    • submit有三种
  • submit有返回值,而execute没有
  • submit方便Exception处理
  • executeExecutor接口中惟必定义的方法
  • submitExecutorService(该接口继承Executor)中定义的方法

关闭

线程池使用完毕,须要对其进行关闭,有两种方法

  • shutdown():再也不继续接收新的任务,执行完成已有任务后关闭

  • shutdownNow():直接关闭,若果有任务尝试中止

相关文章
相关标签/搜索