线程池的作用:
1、减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务;
2、可以根据系统的承受能力,调整线程池的数量,防止消耗过多的内存;
Java通过ExecutorService提供了四种线程池:
1、Executors.newFixedThreadPool(5) :
创建一个定长的线程池,可控制线程最大并发数,超出的线程会在阻塞队列中等待,
线程可重复利用,默认没有线程回收时间,除非线程出现异常导致销毁,那么线程池会补充一个新的线程。
2、Executors.newCachedThreadPool():
创建一个可缓存的线程池,没有长度限制,默认60s回收空闲线程。优先使用回收的空闲线程处理任务,若无,则新建线程。
注意:该线程池没有长度限制,在使用时需要注意内存泄露、溢出的问题。
3、Executors.newScheduledThreadPool(5);
创建一个定长的线程池,支持定时及周期性任务执行
4、Executors.newSingleThreadExecutor();
创建一个单线程的线程池,它只会用唯一的线程来执行任务,任务按照指定顺序执行。如果线程出现异常导致销毁,则创建新的线程。
newFixedThreadPool使用例子:
public static void main(String[] args) {
fixedThreadPool();
}
private static void fixedThreadPool() {
ExecutorService executor = Executors.newFixedThreadPool(5);
for (int i = 0; i < 10; i++) {
executor.execute(new Thread() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "===运行中");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
executor.shutdown();
while (!executor.isTerminated()) {
}
System.out.println("结束.");
}
结果:
pool-N 线程池序列号
thread-M 工作线程序列号
pool-1-thread-1===运行中
pool-1-thread-5===运行中
pool-1-thread-3===运行中
pool-1-thread-4===运行中
pool-1-thread-2===运行中
pool-1-thread-1===运行中
pool-1-thread-3===运行中
pool-1-thread-2===运行中
pool-1-thread-5===运行中
pool-1-thread-4===运行中
结束.
newFixedThreadPool源码简单解析:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
这里是创建一个定长的线程池
=> corePoolSize:核心线程数,也可理解为最小线程数,即线程池中也会保留的线程数量。
=> maximumPoolSize:线程池中允许最大的线程数,默认跟核心数是一样的。
=> keepAliveTime:如果线程数超过了核心线程数,过量的线程在关闭前等待新任务的最大时间
=> unit:时间单位
=> workQueue:任务阻塞队列,LinkedBlockingQueue基于链表结点的无界队列,FIFO
=> defaultThreadFactory:使用线程工厂创建新线程。
线程名的格式:pool-N-thread-M,N表示工厂的序列号,M表示线程的序列号
=> handler:处理因为线程边界和队列容量导致的堵塞
execute方法:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
int c = ctl.get();
//1、如果工作线程数量少于核心线程数量,则启动一个新的工作线程并把传进来的Runnable做为任务。
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
//2、如果工作线程都在运行则把任务放入到队列中,
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
// 2.1、检查我们是否应该添加线程或者停止。
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
//3、如果不能添加队列任务,这里尝试一次往队列中添加任务,如果添加失败的话,用拒绝策略来处理。
else if (!addWorker(command, false))
reject(command);
}
Worker中的执行方法:
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
// getTask() 从队列中获取任务
while (task != null || (task = getTask()) != null) {
w.lock();
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())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
Runnable r = timed ?
workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}