程序启动一个新线程的成本是比较高的,因为涉及操作系统的交互.而使用线程池可以很好的提升性能
尤其在程序中要创建大量生存期很短的线程时,更应该考虑线程池的使用

创建线程池

在意识到需要使用线程池提升效率以后,如何创建线程池呢?
在JDK5版本,提供了一个Executors类来产生线程池,这是一个工厂类

Executors类的常用方法

创建线程池相关

1
2
3
4
5
6
7
8
9
10
//创建一个线程池
public static ExecutorService newCachedThreadPool()
//指定创建线程池的数量
public static ExecutorService newFixedThreadPool(int nThreads)
//单个线程的线程池
public static ExecutorService newSingleThreadExecutor()

public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory)
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory)
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory)

ExecutorService接口

这是一个线程池对象的接口,其中提供了线程池的操作方法
1
2
3
4
5
6
7
Future<?> submit(Runnable task)//提交一个Runnable任务用于执行
<T> Future<T> submit(Runnable task, T result)
<T> Future<T> submit(Callable<T> task)
void shutdown()//执行以前提交的任务,但不会接受新任务
List<Runnable> shutdownNow()//直接停止所有任务,返回还未开始的任务集合
boolean isShutdown()//线程池是否关闭
boolean isTerminated()//关闭后任务是否都以完成

submit相关

首先是测试使用newCachedThreadPool()创建线程池,测试默认线程池数量
从这里发现,线程池的默认名字和线程不同,默认从1开始
简单的案例 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Runnable runnable1 = new Runnable() {
@Override
public void run() {
for (int i =0;i<10;i++) {
System.out.println(Thread.currentThread().getName() + " " + i);
try {
Thread.sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
ExecutorService threadPool1 = Executors.newCachedThreadPool();
for (int i = 0;i<100000;i++)
threadPool1.submit(runnable1);
结果很不理想,并未测试出默认线程池数量,因为再往上加估计要爆内存了
执行结果如下 : 

终止执行的情况如下,电脑内存16G...10万都撑不住....

接下来是创建指定线程数量的线程池,修改代码如下 :
1
2
3
4
//匿名内部类睡眠时间修改为100
ExecutorService threadPool2 = Executors.newFixedThreadPool(2);
for (int i = 0; i<5;i++)
threadPool2.submit(runnable1);
执行代码可以发现其只有两个线程,而且当所有线程全执行完以后程序也不会停止
执行结果如下 :

使用newSingleThreadExecutor()创建单个线程的线程池
执行结果如下 :

shutdown相关

我们对shutdown相关的方法进行测试
测试代码如下 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
Runnable runnable1 = new Runnable() {
private String name = "拾荒者";
@Override
public void run() {
for (int i =0;i<3;i++) {
System.out.println(this + " "+ name + i);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
}
@Override
public String toString() {return Thread.currentThread().getName();}
};
ExecutorService threadPool1 = Executors.newCachedThreadPool();
ExecutorService threadPool2 = Executors.newFixedThreadPool(2);
for (int i = 0; i<5;i++)
threadPool2.submit(runnable1);
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
threadPool2.shutdown();
// List<Runnable> list = threadPool2.shutdownNow();
System.out.println(threadPool2.isShutdown());
System.out.println(threadPool2.isTerminated());
try {
// Thread.sleep(2);
// System.out.println(threadPool2.isTerminated());
Thread.sleep(1010);
threadPool2.submit(runnable1);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(threadPool2.isTerminated());
/*for (Runnable r :
list) {
System.out.println(r);
threadPool1.submit(r);
}*/
首先执行shutdown方法,可以发现
    线程池会将之前提交的线程执行完毕
    shutdown以后线程池就处于停止状态isShutdown()返回为true
    isTerminated(结束)在线程运行完毕之前为false,运行完为true
    停止之后不能在提交(执行)线程,否则抛RejectedExecutorException 拒绝执行异常
        RejectedExecutorException属于运行时异常
执行结果如下 :

然后执行shutdownNow方法以及相关代码,可以发现
    线程池在该方法执行时直接停止所有线程操作
        故而抛出了两个InterruptException中断异常
        且被直接停止的线程不会被返回
    未执行的线程会被返回,以List集合形式
    而isTerminated结束状态除了被停止的瞬间,其余时刻就为true
    停止之后不能执行线程,一个道理
    返回的未执行对象和原对象大致相同
        返回的是FutureTask类型,可以继续运行这些未执行的线程
    执行结果如下 :