详解Java中的线程池

1.简介

使用线程池可以避免线程的频繁创建以及销毁。

JAVA中提供的用于实现线程池的API:

Executor、ExecutorService、AbstractExecutorService、ThreadPoolExecutor、ForkJoinPool都位于java.util.concurrent包下。

*ThreadPoolExecutor、ForkJoinPool为线程池的实现类。

2.Executor

public interface Executor {

  /**
   * 向线程池提交一个任务,交由线程池去执行
   */
  void execute(Runnable command);

}

*该接口声明了execute(Runnable command)方法,负责向线程池中提交一个任务。

3.ExecutorService接口

public interface ExecutorService extends Executor {

  /**
   * 关闭线程池(等待队列中的任务被执行完毕)
   */
  void shutdown();

  /**
   * 立刻关闭线程池(不执行队列中的任务,并尝试中断当前执行的任务)
   */
  List<Runnable> shutdownNow();

  /**
   * 判断线程池是否处于shutdown状态.
   */
  boolean isShutdown();

  /**
   * 判断线程池是否处于terminated状态.
   */
  boolean isTerminated();

  /**
   * 若在指定时间内线程池处于terminated状态则立即返回true,否则超过时间后仍未为terminated状态则返回false.
   */
  boolean awaitTermination(long timeout, TimeUnit unit) throws InterruptedException;

  /**
   * 向线程池提交一个任务并返回包含指定类型的Future(根据Callable的泛型)
   */
  <T> Future<T> submit(Callable<T> task);

  /**
   * 向线程池提交一个任务并指定任务执行结果的类型,返回包含指定类型的Future.
   */
  <T> Future<T> submit(Runnable task, T result);

  /**
   * 向线程池提交一个任务并返回未知类型的Future.
   */
  Future<?> submit(Runnable task);

  /**
   * 向线程池提交多个任务并返回指定类型的Future列表.
   */
  <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks) throws InterruptedException;

  /**
   * 向线程池提交多个任务并返回指定类型的Future列表,如果在指定时间内没有执行完毕则直接返回.
   */
  <T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException;

  /**
   * 向线程池提交多个任务,当任意一个任务执行完毕后返回指定类型的Future.
   */
  <T> T invokeAny(Collection<? extends Callable<T>> tasks) throws InterruptedException, ExecutionException;

  /**
   * 向线程池提交多个任务,在指定时间内,当任意一个任务执行完毕后返回指定类型的Future,若超时则抛出异常.
   */
  <T> T invokeAny(Collection<? extends Callable<T>> tasks, long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
public interface Future<V> {

  /**
   * 中断任务的执行
   */
  boolean cancel(boolean mayInterruptIfRunning);

  /**
   * 判断任务是否中断成功
   */
  boolean isCancelled();

  /**
   * 判断任务是否执行完成
   */
  boolean isDone();

  /**
   * 获取任务的执行结果直到任务执行完毕(阻塞线程)
   */
  V get() throws InterruptedException, ExecutionException;

  /**
   * 获取任务的执行结果,若在指定时间内任务仍然没有执行完毕则抛出TimeoutException
   */
  V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}

*execute()方法不能获取任务的执行结果,而submit()方法能够根据返回的Future实例获取任务的执行结果。

4.ThreadPoolExecutor

corePoolSize:线程池中核心线程的数量。

maximumPoolSize:线程池中最大线程数。

keepAliveTime:线程的空闲时间。

unit:修饰线程空闲时间的单位。

workQueue:任务队列。

threadFactory:线程工厂,用于创建线程。

handler:当队列已满且当前线程数已达到所允许的最大值时的处理策略。

*线程池中的线程包括核心线程以及普通线程,核心线程一旦创建后直到线程池被关闭前都就不会被销毁,而普通线程会因为到达空闲时间而被销毁。

构造方法:

public ThreadPoolExecutor(int corePoolSize,
             int maximumPoolSize,
             long keepAliveTime,
             TimeUnit unit,
             BlockingQueue<Runnable> workQueue,
             ThreadFactory threadFactory,
             RejectedExecutionHandler handler)

BlockingQueue的类型 

BlockingQueue提供了ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue等实现类。

1.ArrayBlockingQueue:使用顺序表的结构进行存储,在使用时需要指定其长度,支持公平锁/非公平锁进行操作。

2.LinkedBlockingQueue:使用链表的结构进行存储,在使用时不需要指定其长度,队列的最大长度为Integer.MAX_VALUE。

3.SynchronousQueue:一个不存储元素的队列,每一个put操作必须等待take操作,否则不能添加元素,支持公平锁和非公平锁。

*这些实现类在进行入队和出队操作时都会进行加锁,以保证在多线程并发访问时数据的安全性。

队列已满且线程数已达到所允许的最大值时的处理策略

RejectedExecutionHandler提供了AbortPolicy、DiscardPolicy、DiscardOlderstPolicy、CallerRunsPolicy四个策略,这四个策略都是ThreadPoolExecutor的静态内部类。

1.AbortPolicy:放弃任务并抛出RejectedExecutionException异常。

2.DiscardPolicy:放弃任务但不抛出异常。

3.DiscardOlderstPolicy: 放弃队头中的任务,然后重新尝试执行新任务。

4.CallerRunsPolicy: 由调用线程来处理该任务。

线程池的状态

private static final int RUNNING  = -1;
private static final int SHUTDOWN  = 0;
private static final int STOP    = 1;
private static final int TIDYING  = 2;
private static final int TERMINATED = 3;

1.RUNING:线程池处于运行状态,此时可以接受新的任务请求,并且执行队列中的任务。

2.SHUTDOWN:线程池处于关闭状态,此时不接受新的任务请求,但会继续执行队列中的任务。

3.STOP:线程池处于禁用状态,此时不接受新的任务请求,并且不会执行队列中的任务。

4.TIDYING:线程池处于整理状态,此时没有正在执行的任务。

5.TERMINATED :线程池处于终止状态。

线程池状态的变化过程

1.当线程池创建后处于RUNNING状态。

2.1 若此时调用了shutdown()方法,那么线程池将处于SHUTDOWN状态,不接受新的任务请求,但会继续执行队列中的任务,当队列中的任务为空且没有正在执行的任务时,线程池的状态为TIDYING。

2.2 若此时调用了shutdownNow()方法,那么线程池将处于STOP状态,不接受新的任务请求并且不执行队列中的任务,此时线程池的状态为TIDYING。

3.当线程池的状态为TIDYING时,当terminated()方法处理完毕后,线程池的状态为TRRMINATED。

任务的执行流程

1.当调用了execute()或者submit()方法向线程池提交一个任务后,首先判断当前线程池中的线程个数是否大于核心线程数。

2.如果当前线程池的线程个数小于核心线程数,则创建一个核心线程来处理任务。

3.如果当前线程池的线程个数大于核心线程数,则将任务放入到队列中,如果放入队列成功,那么该任务将等待被空闲的线程处理,如果放入队列失败(队满),则判断当前线程池中的线程个数是否达到所允许的最大值,若未达到则创建一个普通线程去处理任务,否则根据预定义的处理策略去进行处理。

5.Executors工具类

JAVA中提供了Executors工具类,用于直接创建Executor。

CacheThreadPool

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

CacheThreadPool创建的都是普通线程(其核心线程数为0)、线程池的最大线程数为Integer.MAX_VALUE、线程的空闲时间为60秒,此方式适合大量耗时短的任务、不适合大量耗时长的任务。

*由于创建的都是普通线程,且空闲时间为60秒,则仍有可能会频繁的创建线程。

FixedThreadPool

public static ExecutorService newFixedThreadPool(int nThreads) {
  return new ThreadPoolExecutor(nThreads, nThreads,0L, TimeUnit.MILLISECONDS,new LinkedBlockingQueue<Runnable>());
}

FixedThreadPool创建的都是核心线程,其线程个数由入参决定,线程不会因为空闲时间而被销毁,适合预知任务数量的业务。

SingleThreadExecutor

public static ExecutorService newSingleThreadExecutor() {
  return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,new LinkedBlockingQueue<Runnable>()));
}

SingleThreadExecutor使用一个核心线程来处理任务。

ScheduledThreadPool

public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
    return new ScheduledThreadPoolExecutor(corePoolSize);
}

*ScheduledThreadPool支持定时执行任务以及固定间隔执行任务。

SingleThreadScheduledExecutor

public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
    return new DelegatedScheduledExecutorService(new ScheduledThreadPoolExecutor(1));
}

*SingleThreadScheduledExecutor支持一个线程的定时执行任务以及固定间隔执行任务。

public interface ScheduledExecutorService extends ExecutorService {

  /**
   * 在指定的延迟时间到达后执行任务一次
   */
  public ScheduledFuture<?> schedule(Runnable command,long delay, TimeUnit unit);

  /**
   * 在指定的延迟时间到达后执行任务一次
   */
  public <V> ScheduledFuture<V> schedule(Callable<V> callable,long delay, TimeUnit unit);

  /**
   * 在指定的初始化延迟时间到达后执行任务一次,往后每隔period时间执行任务一次.
   */
  public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit);

  /**
   * 在指定的初始化延迟时间到达后执行任务一次,往后每次任务执行完毕后相隔delay时间执行任务一次.
   */
  public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,long initialDelay,long delay,TimeUnit unit);

}

WorkStealingPool

public static ExecutorService newWorkStealingPool(int parallelism) {
    return new ForkJoinPool(parallelism,ForkJoinPool.defaultForkJoinWorkerThreadFactory,null, true);
}

WorkStealingPool创建一个并行级别的线程池,同一时刻最多只能有指定个数个线程正在执行任务,创建时直接指定同一时刻最多能允许的并行执行的线程个数即可,如果不传则使用CPU的核数。

newWorkStealingPool方法内部返回一个ForkJoinPool实例,ForkJoinPool是JAVA7新提供的线程池,同样继承AbstactExecutorService。

*作用类似于Semaphore。

以上所述是小编给大家介绍的Java中的线程池详解整合,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • Java实现终止线程池中正在运行的定时任务

    最近项目中遇到了一个新的需求,就是实现一个可以动态添加定时任务的功能.说到这里,有人可能会说简单啊,使用quartz就好了,简单粗暴.然而quartz框架太重了,小项目根本不好操作啊.当然,也有人会说,jdk提供了timer的接口啊,完全够用啊.但是我们项目的需求完全是多线程的模型啊,而timer是单线程的,so,楼主最后还是选择了jdk的线程池. 线程池是什么 Java通过Executors提供四种线程池,分别为: newCachedThreadPool :创建一个可缓存线程池,如果线程池长度

  • Java ExecutorService四种线程池使用详解

    1.引言 合理利用线程池能够带来三个好处.第一:降低资源消耗.通过重复利用已创建的线程降低线程创建和销毁造成的消耗.第二:提高响应速度.当任务到达时,任务可以不需要的等到线程创建就能立即执行.第三:提高线程的可管理性.线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控.但是要做到合理的利用线程池,必须对其原理了如指掌. 2.线程池使用 Executors提供的四种线程 1.newCachedThreadPool创建一个可缓存线程池

  • java线程池使用后到底要关闭吗

    线程池做什么 网络请求通常有两种形式: 第一种,请求不是很频繁,而且每次连接后会保持相当一段时间来读数据或者写数据,最后断开,如文件下载,网络流媒体等. 另一种形式是请求频繁,但是连接上以后读/写很少量的数据就断开连接.考虑到服务的并发问题,如果每个请求来到以后服务都为它启动一个线程,那么这对服务的资源可能会造成很大的浪费,特别是第二种情况. 因为通常情况下,创建线程是需要一定的耗时的,设这个时间为T1,而连接后读/写服务的时间为T2,当T1>>T2时,我们就应当考虑一种策略或者机制来控制,使

  • Java手写线程池的实现方法

    本文实例为大家分享了Java手写线程池的实现代码,供大家参考,具体内容如下 1.线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动这些任务.线程池线程都是后台线程. 2.线程池简易架构 3.简易线程池代码(自行优化) import java.util.List; /** * 线程接口 * * @Author yjian * @Date 14:49 2017/10/14 **/ public interface IThreadPool { //加入任务 void ex

  • java简单实现多线程及线程池实例详解

    本文为大家分享了java多线程的简单实现及线程池实例,供大家参考,具体内容如下 一.多线程的两种实现方式 1.继承Thread类的多线程 /** * 继承Thread类的多线程简单实现 */ public class extThread extends Thread { public void run(){ for(int i=0;i<100;i++){ System.out.println(getName()+"-"+i); } } public static void mai

  • Java四种常用线程池的详细介绍

    一. 线程池简介 1. 线程池的概念: 线程池就是首先创建一些线程,它们的集合称为线程池.使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务. 2. 线程池的工作机制 2.1 在线程池的编程模式下,任务是提交给整个线程池,而不是直接提交给某个线程,线程池在拿到任务后,就在内部寻找是否有空闲的线程,如果有,则将任务交给某个空闲的线程

  • 详解Java中的线程池

    1.简介 使用线程池可以避免线程的频繁创建以及销毁. JAVA中提供的用于实现线程池的API: Executor.ExecutorService.AbstractExecutorService.ThreadPoolExecutor.ForkJoinPool都位于java.util.concurrent包下. *ThreadPoolExecutor.ForkJoinPool为线程池的实现类. 2.Executor public interface Executor { /** * 向线程池提交一个

  • 详解Java中的线程模型与线程调度

    JAVA线程模型 线程的实现主要有3种方式: 使用内核线程实现(1:1) 使用用户线程实现(1:N) 使用用户线程加轻量级进程实现(N:M) 使用内核线程实现(Kernel-Level Thread, KLT)(1:1) 内核线程就是直接由操作系统内核支持的线程,这种线程由内核来完成线程的切换,内核通过操作调度器对线程进行调度,并负责将线程的任务映射到各个处理器上. 程序一般不会直接去使用内核,而是去使用线程的一种高级接口--轻量级进程(Light Weight Process,LWP),轻量级

  • 详解Java中的线程让步yield()与线程休眠sleep()方法

    线程让步: yield() yield()的作用是让步.它能让当前线程由"运行状态"进入到"就绪状态",从而让其它具有相同优先级的等待线程获取执行权:但是,并不能保证在当前线程调用yield()之后,其它具有相同优先级的线程就一定能获得执行权:也有可能是当前线程又进入到"运行状态"继续运行! 示例: class ThreadA extends Thread{ public ThreadA(String name){ super(name); }

  • 详解Java如何关闭线程以及线程池

    目录 前言 1. 关闭线程 1.1 volatile关键字 1.2 intrrrupt()方法 2.关闭线程池 2.1 shutdownNow()方法 2.2 shutdown()方法 前言 这个问题是一个高频的面试题 而且在印象中是由stop方法执行或者终端中的kill杀死 但是这些方法直接简单粗暴,很不安全,而且也不推广 不使用stop的方法 之所以不安全不推广是因为: stop方法不管线程逻辑是否完整,都会终止当前正在运行的线程 会破坏其原子逻辑(多线程加了锁之解决资源共享,但是stop会

  • 详解Java中的sleep()和wait()的区别

    详解Java中的sleep()和wait()的区别 对于sleep()方法,我们首先要知道该方法是属于Thread类中的.而wait()方法,则是属于Object类中的. sleep()方法导致了程序暂停执行指定的时间,让出cpu该其他线程,但是他的监控状态依然保持者,当指定的时间到了又会自动恢复运行状态. 在调用sleep()方法的过程中,线程不会释放对象锁. 而当调用wait()方法的时候,线程会放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象调用notify()方法后本线程才进入对象

  • 详解java中的static关键字

    Java中的static关键字可以用于修饰变量.方法.代码块和类,还可以与import关键字联合使用,使用的方式不同赋予了static关键字不同的作用,且在开发中使用广泛,这里做一下深入了解. 静态资源(静态变量与静态方法) 被static关键字修饰的变量和方法统一属于类的静态资源,是类实例之间共享的.被static关键字修饰的变量.方法属于类变量.类方法,可以通过[类名.变量名].[类名.方法名]直接引用,而不需要派生一个类实例出来. 静态资源分类存放的好处 JDK把不同的静态资源放在了不同的

  • 详解java中DelayQueue的使用

    简介 今天给大家介绍一下DelayQueue,DelayQueue是BlockingQueue的一种,所以它是线程安全的,DelayQueue的特点就是插入Queue中的数据可以按照自定义的delay时间进行排序.只有delay时间小于0的元素才能够被取出. DelayQueue 先看一下DelayQueue的定义: public class DelayQueue<E extends Delayed> extends AbstractQueue<E> implements Bloc

  • 详解Java中的锁Lock和synchronized

    一.Lock接口 1.Lock接口和synchronized内置锁 a)synchronized:Java提供的内置锁机制,Java中的每个对象都可以用作一个实现同步的锁(内置锁或者监视器Monitor),线程在进入同步代码块之前需要或者这把锁,在退出同步代码块会释放锁.而synchronized这种内置锁实际上是互斥的,即没把锁最多只能由一个线程持有. b)Lock接口:Lock接口提供了与synchronized相似的同步功能,和synchronized(隐式的获取和释放锁,主要体现在线程进

  • 详解Java中CountDownLatch异步转同步工具类

    使用场景 由于公司业务需求,需要对接socket.MQTT等消息队列. 众所周知 socket 是双向通信,socket的回复是人为定义的,客户端推送消息给服务端,服务端的回复是两条线.无法像http请求有回复. 下发指令给硬件时,需要校验此次数据下发是否成功. 用户体验而言,点击按钮就要知道此次的下发成功或失败. 如上图模型, 第一种方案使用Tread.sleep 优点:占用资源小,放弃当前cpu资源 缺点: 回复速度快,休眠时间过长,仍然需要等待休眠结束才能返回,响应速度是固定的,无法及时响

  • 详解Java中ThreadLocal类型及简单用法

    目录 1 基本概念 2 简单使用 3 应用场景 4 底层原理 4.1 set(Object) 4.2 get() 4.3 remove() 4.4 ThreadLocalMap 5 内存泄漏隐患和防止策略 5.1 为什么会发生内存泄漏? 5.2 怎样防止内存泄漏? 1 基本概念 ThreadLocal类提供了线程局部变量.这些变量与普通变量的不同之处在于,每个访问一个变量(通过其get或set方法)的线程都有自己的.独立初始化的变量副本.ThreadLocal实例通常是希望将状态与线程关联起来的

随机推荐