前言

当项目中有频繁创建线程的场景时,往往会用到线程池来提高效率。所以,线程池在项目开发过程中的出场率是很高的。

那线程池是怎么工作的呢?它什么时候创建线程对象,如何保证线程安全...

什么时候创建线程对象

当实例化线程池对象时,并没有预先创建corePoolSize个线程对象,而是在调用executesubmit提交任务时,才会创建线程对象。

工作流程

public void execute(Runnable command) {
int c = ctl.get();
// 1. 如果核心线程数没有用完,则让核心线程执行任务
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
// 2. 核心线程用完后,尝试添加到等待队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
// 3. 等待队列满后,尝试创建临时线程执行任务;
// 如果创建临时线程失败,即达到了最大线程数,则采用拒绝策略处理
else if (!addWorker(command, false))
reject(command);
}

如何保存线程池状态

// 线程池采用状态压缩的思想,通过 32 个 bit 位来存储线程池状态和工作线程数量
// 其中,高 3 位存储线程池状态,低 29 位存储工作线程数量 // 表示工作线程数量位数
private static final int COUNT_BITS = Integer.SIZE - 3;
// 线程池最大容量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// 通过线程安全的 atomic 对象来存储线程池状态
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); private static int ctlOf(int rs, int wc) { return rs | wc; }

如何保证线程安全

1、对于一些需要线程共享的成员变量,通过volatile修饰

2、线程池状态和工作线程数的修改通过AtomicInteger类自带的线程安全方法实现

3、工作线程Worker通过线程不安全的HashSet存储,每次操作HashSet时都通过一个可重入锁ReentrantLock保证线程安全,ReentrantLock还用来保证访问一些线程不安全的变量,比如

/**
* Tracks largest attained pool size. Accessed only under
* mainLock.
*/
private int largestPoolSize; /**
* Counter for completed tasks. Updated only on termination of
* worker threads. Accessed only under mainLock.
*/
private long completedTaskCount;

4、Worker实现了AQS类,保证线程安全地操作工作线程WorkerWorker通过设置AQS#state来实现加锁和释放锁,当state == 0时,表示没有上锁;当state == 1时,表示上锁,通过CAS方式实现线程安全

protected boolean tryAcquire(int unused) {
if (compareAndSetState(0, 1)) {
setExclusiveOwnerThread(Thread.currentThread());
return true;
}
return false;
} public void lock() { acquire(1); }
public boolean tryLock() { return tryAcquire(1); }
public void unlock() { release(1); }
public boolean isLocked() { return isHeldExclusively(); }

内置线程池

1、SingleThreadPool

单线程池,只使用一个线程执行任务,阻塞队列LinkedBlockingQueue最大容量为Integer.MAX_VALUE

public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}

2、FixedThreadPool

固定大小的线程池,当创建的线程数量达到maxPoolSize,就不再创建线程,阻塞队列LinkedBlockingQueue最大容量为Integer.MAX_VALUE

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

3、CachedThreadPool

缓存线程池,核心线程数量为0,所以任务都通过创建临时线程来执行,临时线程空闲回收时间为60秒,当线程池空闲时,临时线程都会被回收,不耗费资源。

阻塞队列SynchronousQueue不会缓存任务,也就是说,新任务进来后会直接被调度执行,如果没有可用的线程了,则会创建新的线程,如果线程数达到maxPoolSize,即Integer.MAX_VALUE,就执行拒绝策略。

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

4、ScheduleThreadPool

定时调度线程池,通过DelayedWorkQueue

public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}

5、WorkStealingThreadPool

工作窃取线程池,当一个处理器忙时,空闲的处理器可以窃取该处理器后续的任务执行。通过ForkJoinPool实现,可设置支持的并行级别。

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

最新文章

  1. cornerstone知识点
  2. 十四、View Port 2.0
  3. java 获取本地电脑的分辨率代码
  4. 20个免费的 AngularJS 资源和开发教程
  5. poj 1276
  6. Python 之 Bunch Pattern
  7. 【sinatra】修改默认ip绑定
  8. *[codility]Country network
  9. 【JS】Intermediate8:jQuery:AJAX
  10. PAT---1005. Spell It Right (20)
  11. 开源的Android开发框架-------PowerFramework使用心得(五)网络请求HTTPRequest
  12. 希腊字母、拉丁字母、Markdown、拼写与读音中英对照表
  13. Vim插件管理 -- Vundle
  14. ERP库位分布看板(库位管理)
  15. JAVA多线程---ThreadLocal&lt;E&gt;
  16. 用js脚本一键下载网页所有图片
  17. Java 将键盘中的输入保存到数组
  18. 洛谷.5283.[十二省联考2019]异或粽子(可持久化Trie 堆)
  19. 【二分图最大匹配】Bullet @山东省第九届省赛 B
  20. web 接口测试入门

热门文章

  1. Skywalking-05:在Skywalking RocketBot上添加监控图表
  2. 性能测试之查看cpu命令
  3. 深入刨析tomcat 之---第12篇 how tomcat works( 第17章 ) 解析catalina.bat 梳理启动流程
  4. windows10激活方法
  5. Drupal 远程代码执行漏洞(CVE-2018-7602)
  6. markdown文档编写基础
  7. .Net Core微服务——Consul(4):主从、集群
  8. docker容器网络bridge
  9. GitHub创建图床
  10. Linux中的DNS主从解析