JAVA多线程之——线程池
2017-04-06 21:42
162 查看
线程池
线程池顾名思义,就是一个放置线程的池子。就跟数据库连接池差不多。线程池通过对并发线程的控制,能有效的节省系统资源的浪费,提高系统的性能。学习线程池,先了解一下线程池的一个基本结构:
Executor
public interface Executor { void execute(Runnable command); }
Executor是一个接口,其中只有一个方法,就是execute方法。所以Executor实际就是一个线程的执行者。
这里就不把子类的所有方法全部列出来,全部学习一遍,大体可以参照API进行学习。这里主要学习线程池ThreadPoolExecutor方法开始学习,来了解学习线程池的一个运行原理。
ThreadPoolExecutor的一些重要属性
//ctl用来表示线程池的运行状态,和线程池中任务的数量。 private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); //进制位。最大整数转换成二进制的位数 private static final int COUNT_BITS = Integer.SIZE - 3; //线程池的最大容量2的29次方减1 private static final int CAPACITY = (1 << COUNT_BITS) - 1; 。 //运行状态,能接收任务,并对已经添加的任务进行处理 private static final int RUNNING = -1 << COUNT_BITS; //不能接收新任务,但能处理已经添加的任务 private static final int SHUTDOWN = 0 << COUNT_BITS; //不能接收新任务,也不能处理已经添加的任务,并且中断已经在处理的任务 private static final int STOP = 1 << COUNT_BITS; //当所有的任务已终止,ctl记录的"任务数量"为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。 private static final int TIDYING = 2 << COUNT_BITS; //线程池彻底终止,就变成TERMINATED状态。 private static final int TERMINATED = 3 << COUNT_BITS; // Packing and unpacking ctl //返回线程池的状态 private static int runStateOf(int c) { return c & ~CAPACITY; } //返回线程池的有效容量 private static int workerCountOf(int c) { return c & CAPACITY; } //初始化ctl private static int ctlOf(int rs, int wc) { return rs | wc; } //线程的阻塞队列 private final BlockingQueue<Runnable> workQueue; //互斥锁 private final ReentrantLock mainLock = new ReentrantLock(); //线程工作集 private final HashSet<Worker> workers = new HashSet<Worker>(); //终止条件 private final Condition termination = mainLock.newCondition(); // 线程池中线程数量曾经达到过的最大值。 private int largestPoolSize; // 已完成任务数量 private long completedTaskCount; //线程工厂 private volatile ThreadFactory threadFactory; //线程被拒绝时的处理策略 private volatile RejectedExecutionHandler handler; // 保持线程存活时间。 private volatile long keepAliveTime; //是否允许"线程在空闲状态时,仍然能够存活 private volatile boolean allowCoreThreadTimeOut; //核心线程池大小 private volatile int corePoolSize; //线程池的最大容量 private volatile int maximumPoolSize;
这么多变量,可能要记住也不是很容易。通过分析Execute方法可以更加好的理解工作原理和这些属性的意义
public void execute(Runnable command) { //执行的线程为空抛出空指针异常。 if (command == null) throw d9f6 new NullPointerException(); //这里整体可以分三步。 //1. 如果当前线程池中任务数量(一个任务可以理解就是一个线程)小于中心池的容量(corePoolSize),就直接为command新建一个线程,加入任务集workers,并启动该线程。 int c = ctl.get(); if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } //2.如果当前任务数量大于了corePoolSize,并且线程池是可运行状态。就把任务加入到任务队列workQueue中。 加入队列之后,再次确认线程池的状态,这个时候状态不是可运行的,那就把任务从队列中删除,并尝试终止线程池。如果是可运行,那么就检查线程池中工作数量是否为0,如果没有了,那么就添加一个任务为空的线程。 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.如果任务数量大于中心池数量,添加对了也失败了(这里队列是BlockingQueue,前面学习过队列有有界队列和无界队列,所以有可能队列满了导致添加失败。活着其它原因),那么就再进行一次尝试添加到任务集中去,如果失败,执行拒绝策略。 else if (!addWorker(command, false)) reject(command); }
通过上面的步骤学习,可以大致的理一下思路,线程池,有一个中心池容量,这个容量没有满,就可以直接添加任务运行,而任务是被 添加到一个HashSet的Worker中。如果满了,就把任务添加到一个BlockingQueue队列中。都失败了,就直接运行一个拒绝策略。所以,就要理解三个东西:
工作集。
任务队列
拒绝策略。
理解了这三个东西,那么大致就可以了解线程池的一个基本原理。
工作集 Worker
//Worker是线程池的一个内部类 集成了AQS,实现了Runnable接口。 private final class Worker extends AbstractQueuedSynchronizer implements Runnable { private static final long serialVersionUID = 6138294804551838833L; final Thread thread; Runnable firstTask; volatile long completedTasks; Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker 运行前,不准中断 this.firstTask = firstTask; //初始任务值 this.thread = getThreadFactory().newThread(this);//通过线程工为当前任务创建一个线程 } public void run() { runWorker(this);//运行 } protected boolean isHeldExclusively() { return getState() != 0; } protected boolean tryAcquire(int unused) { if (compareAndSetState(0, 1)) { setExclusiveOwnerThread(Thread.currentThread()); return true; } return false; } protected boolean tryRelease(int unused) { setExclusiveOwnerThread(null); setState(0); return true; } public void lock() { acquire(1); } public boolean tryLock() { return tryAcquire(1); } public void unlock() { release(1); } public boolean isLocked() { return isHeldExclusively(); } void interruptIfStarted() { Thread t; if (getState() >= 0 && (t = thread) != null && !t.isInterrupted()) { try { t.interrupt(); } catch (SecurityException ignore) { } } } }
工作集的就是把任务通过线程工厂创建一个该任务的线程并运行。
* 任务队列*
从定义上看我们知道任务队列是一个BlockingQueue。所以线程池中的任务队列可以是任意BlockingQueue的子类。但是常用线程池中常用的的是ArrayBlockingQueue,LinkedBlockingQueue,SynchronousQueue .下一节会学习重用的线程池类型。
拒绝策略
AbortPolicy – 当任务添加到线程池中被拒绝时,它将抛出 RejectedExecutionException 异常。
CallerRunsPolicy – 当任务添加到线程池中被拒绝时,会在线程池当前正在运行的Thread线程池中处理被拒绝的任务。
DiscardOldestPolicy – 当任务添加到线程池中被拒绝时,线程池会放弃等待队列中最旧的未处理任务,然后将被拒绝的任务添加到等待队列中。
DiscardPolicy – 当任务添加到线程池中被拒绝时,线程池将丢弃被拒绝的任务。
大致了解学习了线程池的一个主要运行过程和基本原理。下一节将会学习JDK自带的几种线程池,更加进一步学习和理解线程池。
相关文章推荐
- Java多线程-新特性-线程池
- JAVA多线程之——线程池
- 跟我学Java多线程——ThreadPoolExecutor(线程池)
- Java多线程和线程池
- Java多线程--四种线程池
- Java多线程-线程池ThreadPoolExecutor构造方法和规则
- Java多线程18:线程池
- JAVA多线程之——线程池
- java多线程之初级探索线程池,线程队列
- java多线程之Executors线程池
- Java多线程和线程池
- Java多线程之线程池
- java多线程之线程池
- Java多线程——线程池
- java多线程之线程池
- java多线程总结五:线程池的原理及实现
- JAVA多线程之——线程池
- Java多线程和线程池
- java多线程(15)--线程池ThreadPoolExecutor使用
- JAVA多线程之——线程池