关于java:霸气这份清华学霸整理的Java线程池笔记2小时从入门到入坟

5次阅读

共计 4374 个字符,预计需要花费 11 分钟才能阅读完成。

Hello,明天给各位童鞋们分享 Java 线程池,连忙拿出小本子记下来吧!

1. 为什么应用线程池

1. 频繁创立和销毁单个线程,浪费资源,并且还会呈现频繁 GC

2. 不足对立治理,各线程相互竞争

2.ThreadPoolExecutor

ThreadPoolExecutor 有四个重载的构造方法,咱们这里来说说参数最多的那一个重载的构造方法,这样大家就晓得其余办法参数的含意了,如下:

public ThreadPoolExecutor(int corePoolSize,

                          int maximumPoolSize,

                          long keepAliveTime,

                          TimeUnit unit,

                          BlockingQueue<Runnable> workQueue,

                          ThreadFactory threadFactory,

                          RejectedExecutionHandler handler) 
  • 各参数具体阐明:

这里是 7 个参数 (咱们在开发中用的更多的是 5 个参数的构造方法),OK,那咱们来看看这里七个参数的含意:

corePoolSize 线程池中外围线程的数量

maximumPoolSize 线程池中最大线程数量

keepAliveTime 非核心线程的超时时长,当零碎中非核心线程闲置工夫超过 keepAliveTime 之后,则会被回收。如果 ThreadPoolExecutor 的 allowCoreThreadTimeOut 属性设置为 true,则该参数也示意外围线程的超时时长

unit 第三个参数的单位,有纳秒、微秒、毫秒、秒、分、时、天等

workQueue 线程池中的工作队列,该队列次要用来存储曾经被提交然而尚未执行的工作。存储在这里的工作是由 ThreadPoolExecutor 的 execute 办法提交来的。

threadFactory 为线程池提供创立新线程的性能,这个咱们个别应用默认即可

handler 回绝策略,当线程无奈执行新工作时(个别是因为线程池中的线程数量曾经达到最大数或者线程池敞开导致的),默认状况下,当线程池无奈解决新线程时,会抛出一个 RejectedExecutionException。

  • workQueue 介绍
    1.ArrayBlockingQueue:这个示意一个规定了大小的 BlockingQueue,ArrayBlockingQueue 的构造函数承受一个 int 类型的数据,该数据表示 BlockingQueue 的大小,存储在 ArrayBlockingQueue 中的元素依照 FIFO(先进先出)的形式来进行存取。

2.LinkedBlockingQueue:这个示意一个大小不确定的 BlockingQueue,在 LinkedBlockingQueue 的构造方法中能够传一个 int 类型的数据,这样创立进去的 LinkedBlockingQueue 是有大小的,也能够不传,不传的话,LinkedBlockingQueue 的大小就为 Integer.MAX_VALUE,源码如下:

3.PriorityBlockingQueue:这个队列和 LinkedBlockingQueue 相似,不同的是 PriorityBlockingQueue 中的元素不是依照 FIFO 来排序的,而是依照元素的 Comparator 来决定存取程序的(这个性能也反映了存入 PriorityBlockingQueue 中的数据必须实现了 Comparator 接口)。

4.SynchronousQueue:这个是同步 Queue,属于线程平安的 BlockingQueue 的一种,在 SynchronousQueue 中,生产者线程的插入操作必须要期待消费者线程的移除操作,Synchronous 外部没有数据缓存空间,因而咱们无奈对 SynchronousQueue 进行读取或者遍历其中的数据,元素只有在你试图取走的时候才有可能存在。咱们能够了解为生产者和消费者相互期待,等到对方之后而后再一起来到。

回绝策略
AbortPolicy:间接回绝,并抛出异样,这也是默认的策略。

CallerRunsPolicy:间接让调用 execute 办法的线程去执行此工作。

DiscardOldestPolicy:抛弃最老的未解决的工作,而后从新尝试执行以后的新工作。

DiscardPolicy:间接抛弃当前任务,然而不抛异样

3. 执行过程

当线程数量未达到 corePoolSize 的时候,就会创立新的线程来执行工作。

当外围线程数已满,就会把工作放到阻塞队列。

当队列已满,并且未达到最大线程数,就会新建非核心线程来执行工作 (重要)。

当队列已满,并且达到了最大线程数,则抉择一种回绝策略来执行。

4. 其余线程池

1.FixedThreadPool

固定大小的线程池,能够指定线程池的大小,该线程池 corePoolSize 和 maximumPoolSize 相等,阻塞队列应用的是 LinkedBlockingQueue,大小为整数最大值。

该线程池中的线程数量始终不变,当有新工作提交时,线程池中有闲暇线程则会立刻执行,如果没有,则会暂存到阻塞队列。对于固定大小的线程池,不存在线程数量的变动。

同时应用无界的 LinkedBlockingQueue 来寄存执行的工作。当工作提交非常频繁的时候,LinkedBlockingQueue 迅速增大,存在着耗尽系统资源的问题。

而且在线程池闲暇时,即线程池中没有可运行工作时,它也不会开释工作线程,还会占用肯定的系统资源,须要 shutdown

2.SingleThreadExecutor

能够看到阻塞队例 应用的是 LinkedBolckingQueue,且默认大小为 Integer.MAX_VALUE,这样的话,如果有大量申请到来,会放入到这个工作队列里,可能会导致 OOM;

3.Executors.newCachedThreadPool()

可缓存线程池,先查看线程池中有没有以前建设的线程,如果有就间接应用,如果没有新建一个线程退出线程池中,可缓存线程池

通常用于执行一些生存期很短的异步型工作; 线程池为无限大,当执行当前任务时上一个工作曾经实现,会复用执行上一个工作的线程,而不必每次新建线程

缓存的线程默认存活 60 秒。线程的外围池 corePoolSize 大小为 0,外围池最大为 Integer.MAX_VALUE, 阻塞队列应用的是 SynchronousQueue。

是一个间接提交的阻塞队列,他总会迫使线程池减少新的线程去执行新的工作。

在没有工作执行时,当线程的闲暇工夫超过 keepAliveTime(60 秒),则工作线程将会终止被回收,当提交新工作时,

如果没有闲暇线程,则创立新线程执行工作,会导致肯定的零碎开销。

如果同时又大量工作被提交,而且工作执行的工夫不是特地快,那么线程池便会新增出等量的线程池解决工作,这很可能会很快耗尽零碎的资源。

4.ScheduledThreadPool

创立一个定长线程池,反对定时及周期性工作执行

定时线程池,该线程池可用于周期性地去执行工作,通常用于周期性的同步数据。

scheduleAtFixedRate: 是以固定的频率去执行工作,周期是指每次执行工作胜利执行之间的距离。

schedultWithFixedDelay: 是以固定的延时去执行工作,延时是指上一次执行胜利之后和下一次开始执行的之前的工夫。

5. 为什么阿里举荐自定义线程池

通过上述源码剖析,咱们发现 newFixedThreadPool 和 newSingleThreadExecutor 办法他们都应用了 LinkedBlockingQueue 的工作队列,LinkedBlockingQueue 的默认大小为 Integer.MAX_VALUE。而 newCachedThreadPool 中定义的线程池大小为 Integer.MAX_VALUE。

所以阿里禁止应用 Executors 创立线程池的起因就是 FixedThreadPool 和 SingleThreadPool 的申请队列长度为 Integer.MAX_VALUE,可能会沉积大量的申请,从而导致 OOM。

CachedThreadPool 容许的创立线程数量为 Integer.MAX_VALUE,可能会创立大量的线程,从而导致 OOM。

6. 其余

1.shutDown() 敞开线程池,不影响曾经提交的工作

2.shutDownNow() 敞开线程池,并尝试去终止正在执行的线程

3.allowCoreThreadTimeOut(boolean value) 容许外围线程闲置超时时被回收

4. 单例模式创立线程池

import com.google.common.util.concurrent.ThreadFactoryBuilder;

import java.util.concurrent.*;

/**

  • 异步工作处理器

*/

public class AsyncTaskExecutor {

/** 线程池放弃 ALIVE 状态线程数 */

public static final int                 CORE_POOL_SIZE      = 10;

/** 线程池最大线程数 */

public static final int                 MAX_POOL_SIZE       = 40;

/** 闲暇线程回收工夫 */

public static final int                 KEEP_ALIVE_TIME     = 1000;

/** 线程池期待队列 */

public static final int                 BLOCKING_QUEUE_SIZE = 1000;

/** 业务申请异步解决线程池 */

private static final ThreadPoolExecutor processExecutor = new ThreadPoolExecutor(

        CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_TIME, TimeUnit.MICROSECONDS,

        new LinkedBlockingQueue<Runnable>(BLOCKING_QUEUE_SIZE),

new TreadFactoryBuilder.setNameFormat(“boomoom-thread-pool-%d”).build(),

new TreadPoolExecutor.DiscardPolicy());

private AsyncTaskExecutor() {}; 

/**

 * 异步工作解决

 *

 * @param task 工作

 */

public void execute(Runnable task) {processExecutor.submit(task);

}


}

懒汉式和饥汉式区别

1. 饿汉式是线程平安的,在类创立的同时就曾经创立好一个动态的对象供零碎应用,当前不再扭转。

2. 懒汉式如果想要线程平安必须用双重测验锁并且对象还必须是 volatile,避免对象指令重排

​好啦,明天的文章就到这里,心愿能帮忙到屏幕前迷茫的你们

正文完
 0