java 线程池
来源:互联网 发布:java web面试题 编辑:程序博客网 时间:2024/06/06 01:08
1、java多线程的使用
1.1 当有多个任务时:
new Thread的弊端如下:
a. 每次new Thread新建对象性能差。
b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom。
c. 缺乏更多功能,如定时执行、定期执行、线程中断。
相比new Thread,Java提供的四种线程池的好处在于:
a. 重用存在的线程,减少对象创建、消亡的开销,性能佳。
b. 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
c. 提供定时执行、定期执行、单线程、并发数控制等功能。
1.2 线程池的种类
newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。
newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。
newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
提交任务时 exucute只接收Runnable接口,且没返回值
submit可以接收Runnable和Callable且有返回值。也可以捕获线程的异常,做相应的处理。execute无法在外部捕获异常
http://blog.csdn.net/yuzhiboyi/article/details/7775266
1.3 定制线程池
/*** @param corePoolSize 核心线程数核心线程会一直存活,及时没有任务需要执行当线程数小于核心线程数时,即使有线程空闲,线程池也会优先创建新线程处理设置allowCoreThreadTimeout=true(默认false)时,核心线程会超时关闭* @param maximumPoolSize 任务队列容量(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行* @param keepAliveTime 线程空闲时间当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=corePoolSize如果allowCoreThreadTimeout=true,则会直到线程数量=0* @param unit 时间单位* @param workQueue 任务队列容量(阻塞队列)当核心线程数达到最大时,新任务会放在队列中排队等待执行* @param threadFactory 创建新线程时使用是构造Thread的方法,你可以自己去包装和传递,主要实现newThread方法即可;* @param handler 拒绝策略* @throws IllegalArgumentException if one of the following holds * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} is null */ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue hreadFactory threadFactory, RejectedExecutionHandler handler )
workQueue的选择:
1.直接提交,默认使用SynchronousQueue。SynchronousQueue收到一个任务,直接提交给线程。如果没有线程可以执行新任务则执行拒绝策略
2.无界队列,如LinkedBlockingQueue,当任务数量达到核心线程数,再添加新任务会缓存,而不会触发maximumPoolSize。当缓存的任务数量过多时,可能导致资源耗尽
3.有界队列,如ArrayBlockingQueue,可以防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O 边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU 使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。
corePoolSize的选择:
http://blog.csdn.net/zhouhl_cn/article/details/7392607
2、源码分析
先看懂线程池的几个状态,在读源码会好很多
线程池有几个运行状态 定义在
//COUNT_BITS = 29private static final int RUNNING = -1 << COUNT_BITS;//1110...private static final int SHUTDOWN = 0 << COUNT_BITS;private static final int STOP = 1 << COUNT_BITS;private static final int TIDYING = 2 << COUNT_BITS;private static final int TERMINATED = 3 << COUNT_BITS;//110...
1 当创建线程池后,初始时,线程池处于RUNNING状态;
2 如果调用了shutdown()方法,则线程池处于SHUTDOWN状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;
3 如果调用了shutdownNow()方法,则线程池处于STOP状态,此时线程池不能接受新的任务,并且会去尝试终止正在执行的任务;
4 当线程池处于SHUTDOWN或STOP状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为TERMINATED状态。
ExecutorService cache = Executors.newCachedThreadPool(); Future future = cache.submit(new Callable<Object>() { @Override public Object call() throws Exception { return null; } });
AbstractExecutorService继承ExecutorService并实现submit方法
public <T> Future<T> submit(Callable<T> task) { if (task == null) throw new NullPointerException(); RunnableFuture<T> ftask = newTaskFor(task); execute(ftask); return ftask; }protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) { return new FutureTask<T>(callable); }
主要将task封装成FutureTask,然后调用Executor的execute方法。
具体的execute方法由AbstractExecutorService的子类ThreadPoolExecutor完成
public void execute(Runnable command) { int c = ctl.get(); //如果小于核心线程数 直接添加并返回 if (workerCountOf(c) < corePoolSize) { if (addWorker(command, true)) return; c = ctl.get(); } //如果线程池正在运行 且任务添加阻塞队列成功 if (isRunning(c) && workQueue.offer(command)) { int recheck = ctl.get(); //再次检测,如果线程池停止,则从队列移除任务 if (! isRunning(recheck) && remove(command)) //如果移除成功 拒绝任务 reject(command); else if (workerCountOf(recheck) == 0) //如果总线程数为0 会启动一个空线程 addWorker(null, false); } //尝试启动薪线程执行任务。如果失败,则表示线程池关闭或队列满,执行拒绝策略 else if (!addWorker(command, false)) reject(command); }
execute的操作主要是调用addWorker执行任务
private boolean addWorker(Runnable firstTask, boolean core) { //如果当前线程数没有超过corePoolSize或maximumPoolSize(由core控制),则调用compareAndIncrementWorkerCount将线程数+1 retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && ! (rs == SHUTDOWN && firstTask == null && ! workQueue.isEmpty())) return false; for (;;) { int wc = workerCountOf(c); if (wc >= CAPACITY || wc >= (core ? corePoolSize : maximumPoolSize)) return false; if (compareAndIncrementWorkerCount(c)) break retry; c = ctl.get(); // Re-read ctl if (runStateOf(c) != rs) continue retry; // else CAS failed due to workerCount change; retry inner loop } }// boolean workerStarted = false; boolean workerAdded = false; Worker w = null; try { //将runnable封装为Worker,在Worker中会创建一个线程 w = new Worker(firstTask); final Thread t = w.thread; if (t != null) { final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { // Recheck while holding lock. // Back out on ThreadFactory failure or if // shut down before lock acquired. int rs = runStateOf(ctl.get()); if (rs < SHUTDOWN || (rs == SHUTDOWN && firstTask == null)) { if (t.isAlive()) // precheck that t is startable throw new IllegalThreadStateException(); //将Work加入workers,它是一个hashset workers.add(w); int s = workers.size(); if (s > largestPoolSize) largestPoolSize = s; workerAdded = true; } } finally { mainLock.unlock(); } //如果添加成功 启动Worker的线程 if (workerAdded) { t.start(); workerStarted = true; } } } finally { if (! workerStarted) addWorkerFailed(w); } return workerStarted; }
addWorker将任务包装成Worker,并调用start()方法启动一个线程
private final class Worker extends AbstractQueuedSynchronizer implements Runnable { /** Thread this worker is running in. Null if factory fails. */ final Thread thread; /** Initial task to run. Possibly null. */ Runnable firstTask; /** Per-thread task counter */ /** * Creates with given first task and thread from ThreadFactory. * @param firstTask the first task (null if none) */ Worker(Runnable firstTask) { setState(-1); // inhibit interrupts until runWorker this.firstTask = firstTask; this.thread = getThreadFactory().newThread(this); } /** Delegates main run loop to outer runWorker */ public void run() { runWorker(this); }}
Worker的构造方法中会调用getThreadFactory()获取一个薪线程,然后调用runWorker方法。
final void runWorker(Worker w) { Thread wt = Thread.currentThread(); Runnable task = w.firstTask; w.firstTask = null; w.unlock(); // allow interrupts boolean completedAbruptly = true; try { while (task != null || (task = getTask()) != null) { w.lock();//加锁 // If pool is stopping, ensure thread is interrupted; // if not, ensure thread is not interrupted. This // requires a recheck in second case to deal with // shutdownNow race while clearing interrupt if ((runStateAtLeast(ctl.get(), STOP) || (Thread.interrupted() && runStateAtLeast(ctl.get(), STOP))) && !wt.isInterrupted()) wt.interrupt(); try { //前置执行 beforeExecute(wt, task); Throwable thrown = null; try { //真正执行任务 task.run(); } catch (RuntimeException x) { thrown = x; throw x; } catch (Error x) { thrown = x; throw x; } catch (Throwable x) { thrown = x; throw new Error(x); } finally { //后置执行 afterExecute(task, thrown); } } finally { task = null; w.completedTasks++; w.unlock(); } } completedAbruptly = false; } finally { //退出线程 processWorkerExit(w, completedAbruptly); } }
线程执行完一个任务,会调用getTask()获取一个新的任务继续执行
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // Are workers subject to culling? //线程多于任务数时,是否结束线程标记 boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { //线程数-1 并返回null结束本线程 if (compareAndDecrementWorkerCount(c)) return null; continue; } try { //timed为true表示当前线程大于核心线程数,或者allowCoreThreadTimeOut为true,作用是如果没有足够的任务,将结束多余的线程 Runnable r = timed ? //设置超时,如果没有任务,将会被结束 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : //如果没有任务,会一直阻塞,直到有任务为止 workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
是否能返回任务,与 timed有很大关系
private void processWorkerExit(Worker w, boolean completedAbruptly) { //如果因为线程异常退出 线程数直接-1 if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted decrementWorkerCount(); final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { completedTaskCount += w.completedTasks; //移除worker,即一个线程 workers.remove(w); } finally { mainLock.unlock(); } //尝试终止线程池 tryTerminate(); int c = ctl.get(); //比STOP小的只有SHUTDOWN if (runStateLessThan(c, STOP)) { if (!completedAbruptly) { int min = allowCoreThreadTimeOut ? 0 : corePoolSize; if (min == 0 && ! workQueue.isEmpty()) min = 1; if (workerCountOf(c) >= min) return; // replacement not needed } addWorker(null, false); } }
final void tryTerminate() { for (;;) { int c = ctl.get(); if (isRunning(c) || runStateAtLeast(c, TIDYING) || (runStateOf(c) == SHUTDOWN && ! workQueue.isEmpty())) return; if (workerCountOf(c) != 0) { // Eligible to terminate //中断线程 interruptIdleWorkers(ONLY_ONE); return; } final ReentrantLock mainLock = this.mainLock; mainLock.lock(); try { if (ctl.compareAndSet(c, ctlOf(TIDYING, 0))) { try { terminated(); } finally { ctl.set(ctlOf(TERMINATED, 0)); termination.signalAll(); } return; } } finally { mainLock.unlock(); } // else retry on failed CAS } }
总结:
1、当可以新建线程时
通过Worker包装新Runnable并启动一个线程,并通过线程的run调用Worker的runWorker方法,并执行真正的任务。如果当前任务执行完,会执行阻塞队列中的任务。
2、如果不需要继续新建线程,则将任务放入阻塞队列。
整体框架是一个命令模式
Executor为抽象命令类,声明了一个execute接口
ThreadPoolExecutor为具体命令类,实现execute,执行接收者的具体方法
Runnable为接收者,做具体操作
只需要实现Runnable,封装性好,调用方便
每一个任务实现一个Runnable 扩展性好
http://blog.csdn.net/with_dream/article/details/77480805
2.2 拒绝策略
调用execute(Runnable command)添加任务时,如果队列满,会执行拒绝策略
public interface RejectedExecutionHandler { void rejectedExecution(Runnable r, ThreadPoolExecutor executor);}
主要有:
AbortPolicy() 抛异常
CallerRunsPolicy() 交由调用者线程处理
DiscardOldestPolicy() 抛弃最老的任务
DiscardPolicy() 不接受新任务
参考:
http://blog.csdn.net/rebirth_love/article/details/51954836
http://blog.csdn.net/pangjiuzala/article/details/49556081
- Java线程:线程池
- java--线程--线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(五):线程池
- Java线程_07_线程池
- Java线程(五):线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(六):线程池
- Java线程(五):线程池
- Java线程(六):线程池
- 局域网其他电脑不能连接SVN服务器
- 【拜小白opencv】22-自适应阈值化操作:adaptiveThreshold()函数
- Python+正则表达式编写多线程百度贴吧网页爬虫
- Week 9 Lecture Notes
- ~APTX4869
- java 线程池
- Input类
- PostgreSQL 9种索引的原理和应用场景
- mysql--6.多表查询
- Docker的各种概念
- k近邻法
- java 中 equals 与 == 的区别
- Unity脚本生命周期常用方法
- 笔试面临的j摄像头问题