在java中,如果我们要使用一个线程, 那么我们可以这样创建一个线程
Thread thread= new Thread() {
@Override
public void run() {
l.add(random.nextInt());
}
};
thread.start();
然而这样做的坏处是, 每次都需要重新创建一个线程, 而非重复利用。 频繁的创建线程和销毁线程往往时间甚至要高于线程执行任务的时间。 那么有没有办法让这些线程可重复性的利用呢? 我的意思是, 让这些线程在执行完之后不销毁, 放在某一个地方。 达到可复用的目的呢? 答案是有的。 java中的 java.util.concurrent包中提供了线程池,来达到线程可复用的目的。 当然。 concurrent包中不止线程池 其中还包括了ConcurrentHashMap BlockingQueue 等一系列线程安全的并发容器。
而java.util.concurrent.ThreadPoolExecutor 就是一个线程池。 他是一个Class、 签名如下:
public class ThreadPoolExecutor extends AbstractExecutorService {
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit, BlockingQueue<Runnable> workQueue);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,RejectedExecutionHandler handler);
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadFactory,RejectedExecutionHandler handler);
...
}
共有四个构造方法:
corePoolSize:核心池的大小,默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
maximumPoolSize:线程池最大线程数, 表示在这个池中最多能够创建多少个线程。
keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。 注意 默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize,即当线程池中的线程数大于corePoolSize时,如果一个线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,直到线程池中的线程数为0;
unit:参数keepAliveTime的时间单位,有7种取值。 具体查看文档。
workQueue: 存储等待执行任务的线程, 提供一个队列进行管理任务线程。 基本的任务队列有几种比如:无界队列 有界队列.
这里需要注意的是:在创建ThreadPoolExecutor初期,线程并不会立即启动。而是等到有任务提交时才启动。 除非你显示调用perstartAllCoreThreads方法。
下面我在我的电脑上写了一个不算严格的测试。 来计算使用线程池和不使用线程池的时间消耗。
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPool {
private static int count=2000;
public void yes_threadpool() {
long startTime = System.currentTimeMillis();
final List<Integer> l= new LinkedList<Integer>();
ThreadPoolExecutor tP= new ThreadPoolExecutor(1,60,1,TimeUnit.SECONDS,
new LinkedBlockingQueue<Runnable>(count));
final Random random= new Random();
for (int i = 0; i <count; i++) {
tP.execute(new Runnable() {
@Override
public void run() {
// TODO Auto-generated method stub
l.add(random.nextInt());
}
});
}
tP.shutdown();
try {
tP.awaitTermination(1,TimeUnit.DAYS);
} catch (Exception e) {
// TODO: handle exception
System.out.println("线程池异常");
e.printStackTrace();
}
System.out.println(System.currentTimeMillis()-startTime);
System.out.println(l.size());
}
public void no_threadpool() {
long startTime = System.currentTimeMillis();
final List<Integer> l = new LinkedList<Integer>();
final Random random= new Random();
for(int i=0; i<count; i++) {
Thread thread= new Thread() {
@Override
public void run() {
l.add(random.nextInt());
}
};
thread.start();
try {
thread.join();
} catch (InterruptedException e) {
// TODO: handle exception
System.out.println("非线程池异常");
e.printStackTrace();
}
}
System.out.println(System.currentTimeMillis()-startTime);
System.out.println(l.size());
}
public static void main(String[] args) {
ThreadPool threadPool = new ThreadPool();
// threadPool.yes_threadpool(); 21
//threadPool.no_threadpool(); 1049
/*
* 测试结果为 使用线程池 21 不使用线程池 1049
*
*/
}
}
大家可以分别测试使用线程池和没使用线程池的时间差别。 在我的电脑上 使用线程池只用了21毫秒。 不使用线程池用了1049毫秒。 我的jdk版本是1.8
当然这只是并发包中提供的一种线程池。 另外还有其中有几种。 如newCachedThreadPool newFixedThreadPool 等。 他们都是由Executors提供。
其中: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。 而 new FixedThreadPool 创建一个定长线程池,它可控制线程最大并发数(可以控制最大并发数, 比如说csdn对于下载资源就有并发控制),超出的线程会在队列中等待。.
最近刚好有点空闲时间就做个总结。 关于java.util.concurrent并发包, 只能说。 很强大。