平时使用java写多线程的时候经常使用到jdk的提供的线程池,线程池的概念人人都知道,非常好理解。但是jdk底层到底如何实现线程池的呢?或者说是通过什么方式达到线程池中线程复用的效果的呢?我也是带着这个疑问去翻阅了一下jdk6的源码。
一、java线程池的小例子
public class MyThread implements Runnable { private int i = 0; MyThread(int i){ this.i = i; } @Override public void run() { try { //线程休眠3秒 Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //打印当前线程执行完毕时候的时间,线程id,线程编号 System.out.println("time:" + new Date() + ", threadId:" + Thread.currentThread().getId() + " ,thread " + i); } } public class ExecutorTestMain { public static void main(String[] args) { System.out.println("Main start"); //创建线程池(线程池固定大小3) ExecutorService executorService = Executors.newFixedThreadPool(3); //生成10个线程丢进线程池中 for (int i = 0; i < 10; i++) { executorService.execute(new MyThread(i)); } System.out.println("Main wait for MyThread"); } }
输出:
Main start Main wait for MyThread time:Mon Sep 01 14:52:44 CST 2014, threadId:10 ,thread 2 time:Mon Sep 01 14:52:44 CST 2014, threadId:9 ,thread 1 time:Mon Sep 01 14:52:44 CST 2014, threadId:8 ,thread 0 time:Mon Sep 01 14:52:47 CST 2014, threadId:8 ,thread 5 time:Mon Sep 01 14:52:47 CST 2014, threadId:9 ,thread 4 time:Mon Sep 01 14:52:47 CST 2014, threadId:10 ,thread 3 time:Mon Sep 01 14:52:50 CST 2014, threadId:8 ,thread 6 time:Mon Sep 01 14:52:50 CST 2014, threadId:9 ,thread 7 time:Mon Sep 01 14:52:50 CST 2014, threadId:10 ,thread 8 time:Mon Sep 01 14:52:53 CST 2014, threadId:8 ,thread 9
从上面一个小例子中,可以看出10个线程一下子全丢进线程中了,但是每次执行只有3个(从执行完成时间和执行的线程id可以看出),3个执行完毕之后,才重新释放出来继续执行后面的。那么jdk是怎么做到这种线程的复用的呢?
二、线程池原理
//Executors 使用不同工厂方法构造不同需求的线程池 ExecutorService executorService = Executors.newFixedThreadPool(3);ExecutorService 继承 Executor,线程池接口,定义了线程池执行的一系列接口。
Executors 线程池的工厂类和工具类,提供了构造线程池的工厂方法以及一些线程池操作的工具方法。
对于不同线程池底层复用线程的原理都是一样的,所以我就以小例子中提到的线程池来看看线程池中复用的原理。
public static ExecutorService newFixedThreadPool(int nThreads) { return new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); }Executors 工厂类实际上就是返回一个ThreadPoolExecutor类,ThreadPoolExecutor实际上就可以理解为我们概念中的池子的概念了。
1. ThreadPoolExecutor中包含的属性(主要的属性,其他的参考源码)
//线程池的状态,定义了一些状态常量都是int型,主要是方便比较,后面代码中会看到 //当前状态 volatile int runState; //接收新的任务,并且处理队列中已有的任务(任务的缓存队列) static final int RUNNING = 0; //不接受新任务了,但是会处理队列中已有的任务 static final int SHUTDOWN = 1; //不接受新任务,不处理队列中已有任务,并且终止正在处理的任务 static final intb STOP = 2; //同STOP,但是所以线程已经终止 static final int TERMINATED = 3; //线程池的缓存队列,用于缓存等待线程的任务 private final BlockingQueue<Runnable> workQueue; //线程池中的所以工作线程(Worker是ThreadPoolExecutor内部类,实现了Runnable,复用的东东,后面详细介绍) private final HashSet<Worker> workers = new HashSet<Worker>(); //空闲线程等待工作的超时设置 private volatile long keepAliveTime; private volatile boolean allowCoreThreadTimeOut; //一般情况下线程池大小,对比于maximumPoolSize来看 private volatile int corePoolSize; //线程池最大线程,当线程池处于RUNNING,poolSize大于等于corePoolSize以及小于maximumPoolSize,并且向缓存队列添加失败的情况下,会启用新的Worker来处理任务,如果poolSize超过了maximumPoolSize,那么任务将被拒绝处理 private volatile int maximumPoolSize; //当前线程数量 private volatile int poolSize; //任务被拒绝处理的处理类 private volatile RejectedExecutionHandler handler; //线程的工厂类,默认实现是DefaultThreadFactory private volatile ThreadFactory threadFactory;
2. executorService.execute(new MyThread(i))
//executorService.execute(new MyThread(i)); public void execute(Runnable command) { if (command == null) throw new NullPointerException(); //1.若果poolSize<corePoolSize,那么启用新的Worker处理任务(addIfUnderCorePoolSize) if (poolSize >= corePoolSize || !addIfUnderCorePoolSize(command)) { //2.poolSize >= corePoolSize ,那么放入任务缓存队列(workQueue.offer) if (runState == RUNNING && workQueue.offer(command)) { //3.runState != RUNNING 则处理后续情况 if (runState != RUNNING || poolSize == 0) ensureQueuedTaskHandled(command); } //4.poolSize >= corePoolSize,放入缓冲失败的情况(workQueue.offer失败) else if (!addIfUnderMaximumPoolSize(command)) reject(command); // is shutdown or saturated } }
3 . 创建新的Worker处理任务
addIfUnderCorePoolSize 和 addIfUnderMaximumPoolSize 处理逻辑一致,下面以addIfUnderCorePoolSize为例说明
private boolean addIfUnderCorePoolSize(Runnable firstTask) { Thread t = null; final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { //addIfUnderMaximumPoolSize这里的if判断条件是poolSize < maximumPoolSize && runState == RUNNING if (poolSize < corePoolSize && runState == RUNNING) //addTread返回的线程是Worker t = addThread(firstTask); } finally { mainLock.unlock(); } if (t == null) return false; //执行线程,会执行到Worker的run方法 t.start(); return true; }
private Thread addThread(Runnable firstTask) { //将任务封装成Worker Worker w = new Worker(firstTask); //创建Worker线程 Thread t = threadFactory.newThread(w); if (t != null) { w.thread = t; workers.add(w); int nt = ++poolSize; if (nt > largestPoolSize) largestPoolSize = nt; } return t; }
4. Worker
Worker就可以理解为线程池中的工作线程了,所有提交上来的线程都是在在这些工作线上上面执行的,那么具体是如何做到执行和复用的呢?下面我们就一探究竟。
Worker实际上是ThreadPoolExecutor的一个内部类,实现了Runnable接口。主要属性:
//初始化Worker的时候第一个任务 private Runnable firstTask; //worker执行的任务数 volatile long completedTasks; //worker所运行的thread对象 Thread thread;
线程执行的时候会执行到Worker中的run方法
public void run() { try { Runnable task = firstTask; firstTask = null; //getTask 就是从缓冲中获取任务,我们可以看到,这个Worker线程会循环执行,不停的从缓冲队列中获取任务执行 while (task != null || (task = getTask()) != null) { //获取任务执行 runTask(task); task = null; } } finally { workerDone(this); } } Runnable getTask() { //从缓存队列中获取任务执行(当然获取的逻辑中涉及到很多当前线程池不同状态的情况) for (;;) { try { int state = runState; if (state > SHUTDOWN) return null; Runnable r; if (state == SHUTDOWN) // Help drain queue r = workQueue.poll(); else if (poolSize > corePoolSize || allowCoreThreadTimeOut) r = workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS); else r = workQueue.take(); if (r != null) return r; if (workerCanExit()) { if (runState >= SHUTDOWN) // Wake up others interruptIdleWorkers(); return null; } // Else retry } catch (InterruptedException ie) { // On interruption, re-check runState } } }
我们都知道,线程执行的过程中都一些自己执行相关的信息,这些信息显然是不可能给其他任务去复用的,所以在执行之前需要去做一些清理工作。
private void runTask(Runnable task) { final ReentrantLock runLock = this.runLock; runLock.lock(); try { /* * Ensure that unless pool is stopping, this thread * does not have its interrupt set. This requires a * double-check of state in case the interrupt was * cleared concurrently with a shutdownNow -- if so, * the interrupt is re-enabled. */ if (runState < STOP && Thread.interrupted() && runState >= STOP) thread.interrupt(); /* * Track execution state to ensure that afterExecute * is called only if task completed or threw * exception. Otherwise, the caught runtime exception * will have been thrown by afterExecute itself, in * which case we don't want to call it again. */ boolean ran = false; //重新初始化thread中的ThreadLocals等信息 beforeExecute(thread, task); try { //任务的执行 task.run(); ran = true; //后置处理 afterExecute(task, null); ++completedTasks; } catch (RuntimeException ex) { if (!ran) afterExecute(task, ex); throw ex; } } finally { runLock.unlock(); } }
相关推荐
### JAVA线程池原理及几种线程池类型的详细介绍 #### 一、线程池的引入背景及重要性 在现代软件开发中,特别是在基于Java的应用程序设计中,线程池技术已经成为提高系统性能和资源利用率的关键手段之一。线程池...
Java concurrency线程池之线程池原理(一)_动力节点Java学院整理,动力节点口口相传的Java黄埔军校
Java线程池是一种高效管理线程的工具,它允许开发者预先创建一组线程,并复用它们来处理任务,从而降低了创建和销毁线程的开销。线程池的使用尤其适用于处理大量短小任务的场景,例如Web服务器、数据库服务器等。在...
JAVA线程池原理实例详解 JAVA线程池是java中的一种高效的线程管理机制,它可以控制线程的创建、销毁和复用,从而提高系统的性能和可靠性。下面我们将详细介绍JAVA线程池的原理、创建、使用方法及相关注意事项。 一...
本文将深入探讨Java线程池的原理、封装以及相关知识点。 ### 1. Java线程池概念 Java线程池由`java.util.concurrent`包中的`ExecutorService`接口和其子类实现。其中,最常用的是`ThreadPoolExecutor`类,它提供了...
理解Java线程池的原理和实现,可以帮助我们更有效地管理并发任务,提升系统性能,同时避免资源浪费和线程安全问题。在实际开发中,合理配置线程池参数,结合业务场景选择合适的线程池类型,是优化系统性能的关键步骤...
Java线程池是一种高效利用系统资源、管理并发执行任务的机制。...总的来说,理解Java线程池的工作原理和实现对于优化并发应用程序至关重要,它可以帮助我们更好地控制系统的并发度,提高系统的响应速度和资源利用率。
Java线程池是一种高效管理线程的工具,它允许开发者预先创建一组线程,当需要执行新任务时,线程池会从池中选择一个空闲线程来执行任务,而不是每次都创建新的线程。线程池的使用有助于减少线程创建和销毁的开销,...
本文将深入探讨Java线程池的工作原理,并通过实际代码示例,展示如何高效地使用线程池。 Java线程池是并发编程中的一个重要工具,它通过减少线程创建和销毁的开销,提高了程序的性能和资源利用率。理解线程池的...
### 自定义实现Java线程池 #### 一、概述 在深入探讨自定义Java线程池之前,我们先简要回顾一下线程池的基本概念及其重要性。线程池是一种多线程处理形式,处理过程中将任务添加到队列,然后在创建线程后自动启动...
总结Java线程池是Java多线程编程的重要工具,通过合理的配置和使用,可以有效地管理和调度线程,提高系统性能。理解线程池的工作原理和参数配置,对于编写高效、稳定的多线程程序至关重要。在实际应用中,应根据任务...
Java线程池是一种高效管理线程的机制,它允许开发者预先设定线程的数量,并通过池化的方式重用已创建的线程,以提高系统性能,减少线程的创建和销毁开销。线程池在Java中是通过`java.util.concurrent`包下的`...
首先希望大家喜欢我制作的文档,如果文档中有什么误解的地方,望告诉一下,5分是也不多,是系统默认的,那么就5分咯,java多线程详解,线程池原理,8种锁,java内存模型......
Java线程池是一种高效管理线程资源的工具,它通过维护一组可重用的线程来减少创建和销毁线程的开销。在Java中,`java.util.concurrent`包提供了`ExecutorService`接口和它的实现类,如`ThreadPoolExecutor`,来支持...
通过剖析Java中线程池的原理,解读Java线程池源码,并给出线程池调用的示例,帮助理解线程池的基本原理。
Java线程池是一种高效管理并发任务的机制,它允许开发者预先配置一定数量的线程,以便在处理多个并发任务时能有效地复用这些线程,从而避免了频繁创建和销毁线程带来的开销。在Java中,`java.util.concurrent`包下的...
Java线程池是一种高效管理线程资源的技术,它允许开发者创建一组可重用的工作线程,从而避免频繁地创建和销毁线程带来的性能开销。线程池在Java中主要通过`java.util.concurrent`包中的`ExecutorService`接口及其...
本文所提及的基于Java线程池技术的数据爬虫设计与实现,不仅涉及到了数据爬虫的原理和架构,还包括了多线程编程的知识点,以及线程池技术在数据爬虫中的具体应用。 首先,数据爬虫的基本原理是模拟用户的点击行为,...
Java线程池是Java并发编程中的重要组成部分,它允许开发者高效地管理多个并发执行的线程,有效地控制系统的资源消耗,提高系统性能和稳定性。在Java中,`java.util.concurrent`包提供了`ExecutorService`接口及其...
鸟之家破解器源码