多线程的软件设计方法确实可以最大限度发挥现代多核处理器的计算能力,提高生产系统的吞吐量和性能。但是如果不加控制和管理的随意使用线程,对系统的性能反而会造成不利影响
一种简单的线程创建和回收方法类似如下代码呈现:
Java
new Thread(new Runnable(){
@Override
public void run(){
//do sth
}
}).start();
1
2
3
4
5
6
newThread(newRunnable(){
@Override
publicvoidrun(){
//do sth
}
}).start();
以上代码创建了一个线程并会在run()方法结束之后,自动回收该线程。在简单的应用中,这段代码并不会有太大问题,但是在生产环境中,系统可能会开启很多线程来支撑应用,当线程数量过大时,就会耗尽CPU和内存资源。同时也会给GC带来很大压力,延长GC的停顿时间。
因此,在实际生产环境中,线程数量必须得到控制。盲目的创建大量线程对系统性能是有伤害的。
为此,就需要引入线程池。线程池的基本功能就是进行线程复用:当系统接受一个提交的任务,需要一个线程时,首先去线程池中查找是否有空余的线程,如果有则直接使用线程池中的线程,如果没有则根据算法决定是创建新线程还是等待空闲线程出现。
这样,在线程频繁调度的场合,可以节约不少系统开销(线程创建和销毁的开销)
以下例子实现了一个简单的线程池:
首先是线程池的实现:
Java
public class ThreadPool
{
private static ThreadPool instance = null;
//空闲的线程队列
private List idleThreads;
//已有的线程总数
private int threadCounter;
private boolean isShutDown = false;
private ThreadPool()
{
this.idleThreads = new ArrayList<>(5);
threadCounter = 0;
}
int getCreatedThreadsCount() {
return threadCounter;
}
//取得线程池的实例
synchronized static ThreadPool getInstance() {
if (instance == null)
instance = new ThreadPool();
return instance;
}
//将线程放入池中
synchronized void repool(PThread repoolingThread)
{
if (!isShutDown)
{
idleThreads.add(repoolingThread);
}
else
{
repoolingThread.shutDown();//关闭线程
}
}
//停止池中所有线程
public synchronized void shutdown()
{
isShutDown = true;
for (PThread idleThread : idleThreads)
{
idleThread.shutDown();
}
}
//执行任务
synchronized void start(Runnable target)
{
PThread thread ;
//如果有空闲线程,则直接使用
if (idleThreads.size() > 0)
{
int lastIndex = idleThreads.size() - 1;
thread = idleThreads.get(lastIndex);
idleThreads.remove(lastIndex);
//立即执行这个任务
thread.setTarget(target);
}
//没有空闲线程,则创建新线程
else
{
threadCounter++;
// 创建新线程,
thread = new PThread(target, "PThread #" + threadCounter, this);
//启动这个线程
thread.start();
}
}
}
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
publicclassThreadPool
{
privatestaticThreadPoolinstance=null;
//空闲的线程队列
privateListidleThreads;
//已有的线程总数
privateintthreadCounter;
privatebooleanisShutDown=false;
privateThreadPool()
{
this.idleThreads=newArrayList<>(5);
threadCounter=0;
}
intgetCreatedThreadsCount(){
returnthreadCounter;
}
//取得线程池的实例
synchronizedstaticThreadPoolgetInstance(){
if(instance==null)
instance=newThreadPool();
returninstance;
}
//将线程放入池中
synchronizedvoidrepool(PThreadrepoolingThread)
{
if(!isShutDown)
{
idleThreads.add(repoolingThread);
}
else
{
repoolingThread.shutDown();//关闭线程
}
}
//停止池中所有线程
publicsynchronizedvoidshutdown()
{
isShutDown=true;
for(PThreadidleThread:idleThreads)
{
idleThread.shutDown();
}
}
//执行任务
synchronizedvoidstart(Runnabletarget)
{
PThreadthread;
//如果有空闲线程,则直接使用
if(idleThreads.size()>0)
{
intlastIndex=idleThreads.size()-1;
thread=idleThreads.get(lastIndex);
idleThreads.remove(lastIndex);
//立即执行这个任务
thread.setTarget(target);
}
//没有空闲线程,则创建新线程
else
{
threadCounter++;
// 创建新线程,
thread=newPThread(target,"PThread #"+threadCounter,this);
//启动这个线程
thread.start();
}
}
}
如果需要使用上述线程池,则需要一个永不退出的线程与之配合。下例中的PThread就是这样一个线程,它的线程主体是一个无限循环,在手动关闭之前永不结束并一直等待新任务提交:
Java
public class PThread extends Thread
{
//线程池
private ThreadPool pool;
//任务
private Runnable target;
private boolean isShutDown = false;
private boolean isIdle = false;
public PThread(Runnable target, String name, ThreadPool pool)
{
super(name);
this.pool = pool;
this.target = target;
}
public Runnable getTarget()
{
return target;
}
public boolean isIdle()
{
return isIdle;
}
public void run()
{
while (!isShutDown)
{
isIdle = false;
if (target != null)
{
// 运行任务
target.run();
}
//任务结束了
isIdle = true;
try
{
//该任务结束后,不关闭线程,而是放入线程池空闲队列
pool.repool(this);
synchronized (this)
{
//线程空闲,等待新的任务到来
wait();
}
}
catch (InterruptedException ie)
{
}
isIdle = false;
}
}
public synchronized void setTarget(java.lang.Runnable newTarget)
{
target = newTarget;
//设置了任务之后,通知run方法,开始执行这个任务
notifyAll();
}
public synchronized void shutDown()
{
isShutDown = true;
notifyAll();
}
}
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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
publicclassPThreadextendsThread
{
//线程池
privateThreadPoolpool;
//任务
privateRunnabletarget;
privatebooleanisShutDown=false;
privatebooleanisIdle=false;
publicPThread(Runnabletarget,Stringname,ThreadPoolpool)
{
super(name);
this.pool=pool;
this.target=target;
}
publicRunnablegetTarget()
{
returntarget;
}
publicbooleanisIdle()
{
returnisIdle;
}
publicvoidrun()
{
while(!isShutDown)
{
isIdle=false;
if(target!=null)
{
// 运行任务
target.run();
}
//任务结束了
isIdle=true;
try
{
//该任务结束后,不关闭线程,而是放入线程池空闲队列
pool.repool(this);
synchronized(this)
{
//线程空闲,等待新的任务到来
wait();
}
}
catch(InterruptedExceptionie)
{
}
isIdle=false;
}
}
publicsynchronizedvoidsetTarget(java.lang.RunnablenewTarget)
{
target=newTarget;
//设置了任务之后,通知run方法,开始执行这个任务
notifyAll();
}
publicsynchronizedvoidshutDown()
{
isShutDown=true;
notifyAll();
}
}
对这个简单的线程池进行测试,开启1200个线程进行工作,消耗时间234 MS,同样的任务采用线程池的实现,仅需要花费174 MS。
注意在未使用线程池的测试中,实际产生线程的数量达到了1200个,而在使用线程池的测试中,因为任务一边被调度一边被执行,所以在整个任务调度过程中,有部分线程因为执行完毕,重新返回线程池中,因此实际生成的线程数量在800个左右。