java线程池知识

一,线程池创建方式

1.Executors

快捷创建线程池的方法:

        ExecutorService FixedThreadPool = Executors.newFixedThreadPool(10);

        ExecutorService CachedThreadPool = Executors.newCachedThreadPool();

        ExecutorService ScheduledThreadPool = Executors.newScheduledThreadPool(10);

        ExecutorService SingleThreadExceutor = Executors.newSingleThreadPool();

优点:快捷,简单
缺点:拒绝策略,阻塞队列闲置线程存活时间等参数,都是用Executors给的默认设置。无法满足特殊需求。
阿里巴巴开发手册,明确提出不允许使用此方法创建线程池,使用ThreadPoolExecutor创建线程 池,并手动设置线程池内线程数量,阻塞队列类型,大小。以及拒绝策略,方便后续维护和理解。

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

这里的任务队列就是使用的 LinkedBlockingQueue 且是无界队列,最大值是 integer 的最大值。如果有很多的任务,会OOM异常。

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

可以看出,各种线程的快捷创建方法,都是由ThreadPoolExecutor实现的。不如直接使用这个创建,别人一看就知道怎么回事, 也方便修改和配置。

2.ThreadPoolExecutor

LinkedBlockingDeque<Runnable> queue = new LinkedBlockingDeque<>(20);
ThreadFactory threadFactory = Executors.defaultThreadFactory();
RejectedExecutionHandler AbortPolicy =new ThreadPoolExecutor.AbortPolicy();

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 4, 2L,
                TimeUnit.SECONDS, queue, threadFactory, AbortPolicy);

这段代码,是自定义线程池,7个参数分别是

  • 1.线程最少线程数,初始线程数 (2)
  • 2.线程池内最大线程数 (4)
  • 3.闲置线程存活时间 (2L)
  • 4.存活时间单位 (TimeUnit.SECONDS)
  • 5.存放人物的队列 (queue)
  • 6.创建线程的工厂 (threadFactory)
  • 7.拒绝策略 (AbortPolicy)

二,线程池类型

1,FixedThreadPool

  • 容量固定的线程池。使用LinkedBlockingQueue作为任务队列,当任务数量大于线程池容量的时候,未执行的任务进入任务等待队列LinkedBlockingQueue中,当线程有空闲的时候,自动从队列中取出任务执行。
  • 使用场景: 大多数情况下,推荐使用的线程池。因为os系统和硬件是有线程上限限制的,不可能去无限的提供线程池操作。

2,CachedThreadPool

  • 缓存线程池。容量 0-Integer.MAX_VALUE,自动根据任务数扩容:如果线程池中的线程数不满足任务执行需求,则创建新的线程并添加到池中。生命周期默认60s,当线程空闲时长到60s的时候,自动终止销毁释放线程,移除线程池。

  • 使用场景 : 可用于测试最高负载量,用于对FixedThreadPool容量的参考。

注意,放入CachedThreadPool的线程不必担心其结束,超过TIMEOUT(默认60s)不活动,其会自动被终止

3,ScheduledThreadPool

  • 定时及周期性的任务执行的线程池,多数情况下可用来替代Timer类。

4,SingleThreadExceutor

  • 单一容量线程池。

5,ForkJoinPool

  • 拆分合并,将一个大的任务,拆分成若干子任务,并最终汇总子任务的执行结果,得到大任务的执行结果。并行执行,采用工作窃取机制,更加有效的利用cpu资源。
5.1、主要类
  • ForkJoinPool : 用于执行Task。任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他未完成工作线程的队列的尾部获取一个任务。

  • ForkJoinTask:ForkJoin任务,提供在任务中执行fork()和join()操作的机制(二叉分逻辑),通常不直接继承ForkJoinTask类,而是继承抽象子类RecursiveTask(有返回结果) 或者 RecursiveAction (无返回结果)。

  • ForkJoinWorkerThread:ForkJoinPool 内部的worker thread,用来具体执行ForkJoinTask。内部有

  • ForkJoinPool.WorkQueue,来保存要执行的 ForkJoinTask。

  • ForkJoinPool.WorkQueue:保存要执行的ForkJoinTask。

5.2、工作窃取机制

1、大任务分割成N个子任务,为避免线程竞争,于是分开几个队列去保存这些子任务,并为每个队列提供一个工作线程去处理其中的任务。工作线程与任务队列一一对应。

2、如果A线程执行完自己队列中的所有任务,如果此时其他队列中还有未执行的任务,则A线程会去窃取一个其他队列的任务来执行。但是,此时两个线程同时访问可能会产生竞争问题,所以,任务队列设计成了双向队列。A线程窃取的时候,从另一端开始执行,尽可能的去避免线程竞争问题。

3、工作窃取机制,充分的利用线程资源,并尽可能的去避免线程间的竞争问题。但是,只能是尽可能避免,并不能规避。例如,双向队列只有一个任务。

三,阻塞队列

1,LinkedBlockingQueue

2,ArrayBlockingQueue

3,SynchronousQueue

4,PriorityBlockingQueue

四,拒绝策略

拒绝策略提供顶级接口 RejectedExecutionHandler ,其中方法 rejectedExecution 即定制具体的拒绝策略的执行逻辑。

jdk默认提供了四种拒绝策略:

1,ThreadPoolExecutor.AbortPolicy()

  • 丢弃任务,并抛出拒绝执行 RejectedExecutionException 异常信息。线程池默认的拒绝策略。必须处理好抛出的异常,否则会打断当前的执行流程,影响后续的任务执行。

2,ThreadPoolExecutor.CallerRunsPolicy

  • 当触发拒绝策略,只要线程池没有关闭的话,则使用调用线程直接运行任务。一般并发比较小,性能要求不高,不允许失败。但是,由于调用者自己运行任务,如果任务提交速度过快,可能导致程序阻塞,性能效率上必然的损失较大

3,ThreadPoolExecutor.DiscardPolicy

  • 直接丢弃,其他啥都没有

4,ThreadPoolExecutor.DiscardOldestPolicy

  • 当触发拒绝策略,只要线程池没有关闭的话,丢弃阻塞队列 workQueue 中最老的一个任务,并将新任务加入

五,线程工厂

1,Executors.defaultThreadFactory

  • ThreadFactory是一个线程工厂。用来创建线程。这里为什么要使用线程工厂呢?其实就是为了统一在创建线程时设置一些参数,如是否守护线程。线程一些特性等,如优先级。通过这个TreadFactory创建出来的线程能保证有相同的特性。它首先是一个接口类,而且方法只有一个。就是创建一个线程。
  public interface ThreadFactory {    
    Thread newThread(Runnable r);  
}  

2,Executors.privilegedThreadFactory

  • 如果在应用程序中需要利用安全策略来控制对某些代码库的访问,那么可以通过Executors.privilegedThreadFactory来制定自己的线程工厂,通过这种方式创建的线程,将与创建privilegedThreadFactory的线程拥有相同的访问权限,AccessControlContext,contextClassLoader.
    如果不使用privilegedThreadFactory,线程池创建的线程将从在需要新线程时调用excute 或submit 的客户端程序中继承访问权限。从而导致令人困惑的安全性异常。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值