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.youkuaiyun.com/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.youkuaiyun.com/zhouhl_cn/article/details/7392607
2、源码分析
先看懂线程池的几个状态,在读源码会好很多
线程池有几个运行状态 定义在
//COUNT_BITS = 29
private 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.youkuaiyun.com/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.youkuaiyun.com/rebirth_love/article/details/51954836
http://blog.youkuaiyun.com/pangjiuzala/article/details/49556081
170万+

被折叠的 条评论
为什么被折叠?



