线程池的作用:
线程池作用就是限制系统中执行线程的数量,减少了创建和销毁线程的次数,根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果。
用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。
每个工作线程都可以被重复利用,可执行多个任务,可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)
创建Java线程
import java.util.LinkedList;
public class ThreadPool extends ThreadGroup {
private boolean isClosed = false;
private LinkedList workQueue;
private static int threadPoolID = 1;
public ThreadPool(int poolSize) {
super(threadPoolID + "");
setDaemon(true);
this.workQueue = new LinkedList();
for (int i = 0; i < poolSize; i++) {
new WorkThread(i).start();
}
}
public synchronized void execute(Runnable task) {
if (this.isClosed) {
throw new IllegalStateException();
}
if (task != null) {
this.workQueue.add(task);
notify();
}
}
private synchronized Runnable getTask(int threadid)
throws InterruptedException {
while (this.workQueue.size() == 0) {
if (this.isClosed) return null;
System.out.println("Work thread " + threadid + " waiting task...");
wait();
}
System.out.println("Work thread " + threadid + " begin executing task...");
return (Runnable) this.workQueue.removeFirst();
}
public synchronized void closePool() {
if (!this.isClosed) {
waitFinish();
this.isClosed = true;
this.workQueue.clear();
interrupt();
}
}
public void waitFinish() {
synchronized (this) {
this.isClosed = true;
notifyAll();
}
Thread[] threads = new Thread[activeCount()];
int count = enumerate(threads);
for (int i = 0; i < count; i++) {
try {
threads[i].join();
} catch (InterruptedException ex) {
ex.printStackTrace();
}
}
}
private class WorkThread extends Thread {
private int id;
public WorkThread(int id) {
super(ThreadPool.this, id + "");
this.id = id;
}
public void run() {
while (!isInterrupted()) {
Runnable task = null;
try {
task = getTask(id);
} catch (InterruptedException ex) {
ex.printStackTrace();
}
if (task == null) return;
try {
task.run();
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}
//For test
public static class ThreadPoolTest {
public static void main(String[] args) throws InterruptedException {
ThreadPool threadPool = new ThreadPool(3);
Thread.sleep(500);
for (int i = 0; i <= 5; i++) {
threadPool.execute(createTask(i));
}
threadPool.waitFinish();
threadPool.closePool();
}
private static Runnable createTask(final int taskID) {
return new Runnable() {
public void run() {
// System.out.println("Task " + taskID + " begin");
System.out.println("Hello world");
// System.out.println("Task " + taskID + " end");
}
};
}
}
}
Java5中的线程池
要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。
- newSingleThreadExecutor:创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。
- newFixedThreadPool:创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。
- newCachedThreadPool:创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。
- newScheduledThreadPool:创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。
- newSingleThreadScheduledExecutor:创建一个单线程的线程池。此线程池支持定时以及周期性执行任务的需求。
线程池类 java.util.concurrent.ThreadPoolExecutorr的常用构造方法为:
ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) ;
注:
corePoolSize: 线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime: 线程池维护线程所允许的空闲时间
unit: 线程池维护线程所允许的空闲时间的单位
workQueue: 线程池所使用的缓冲队列
handler: 线程池对拒绝任务的处理策略
当一个任务通过execute(Runnable)方法欲添加到线程池时:
如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。
即处理任务的优先级为:
核心线程corePoolSize、任务队列workQueue、最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
ExecutorService在Executor的基础上增加了一些方法,其中有两个核心的方法:
- Future<?> submit(Runnable task)
- <T> Future<T> submit(Callable<T> task)
这两个方法都是向线程池中提交任务,它们的区别在于Runnable在执行完毕后没有结果,Callable执行完毕后有一个结果。这在多个线程中传递状态和结果是非常有用的。另外他们的相同点在于都返回一个Future对象。Future对象可以阻塞线程直到运行完毕(获取结果,如果有的话),也可以取消任务执行,当然也能够检测任务是否被取消或者是否执行完毕。
在没有Future之前我们检测一个线程是否执行完毕通常使用Thread.join()或者用一个死循环加状态位来描述线程执行完毕。现在有了更好的方法能够阻塞线程,检测任务执行完毕甚至取消执行中或者未开始执行的任务。