想象一个场景,一个工厂在生产玩具,在一个车间里,有几个工人,每次生产部件准备好车间外的人就将部件放到车间的一个桌子上,工人每次做完一个玩具就从桌子上取部件。在这里,注意到,部件并不是直接交给工人的,另外一点,工人并不是做完一个部件就回家换个新人,后者在现实有点滑稽,但是在程序中却对应一个典型的线程使用方法:线程池。
所谓线程池,就是对线程的复用,当线程执行完任务之后就继续取其他任务执行,而不是销毁启动新线程执行其他任务。因为线程的启动对于系统性能开销比较大,所以这样对于系统性能的提高很有好处。
来个典型程序范例:
首先是请求,即玩具的部件
public class Request {
private final String name;
private final int number;
private static final Random random = new Random();
public Request(String name, int number) {
this.name = name;
this.number = number;
}
public void execute(){
System.out.println(Thread.currentThread().getName()+"execute"+this);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
@Override
public String toString() {
return "Request from" + name + "No."+ number;
}
也就是拥有name和number并且 execute 的时候打印出字段的一个简单类。
然后是ClientThread,负责将请求放入RequestQueue中,即将部件放到桌子上。
public class ClientThread extends Thread{
private final Channel channel;
private static final Random random = new Random();
public ClientThread(Channel channel,String name) {
super(name);
this.channel = channel;
}
@Override
public void run() {
for (int i = 0; true; i++) {
Request request = new Request(getName(),i);
channel.putRequest(request);
try {
Thread.sleep(random.nextInt(1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
C
hannel类,可以当做车间
public class Channel {
private static final int MAX_REQUEST = 100;
private final Request[] requestQueue;
private int tail;
private int head;
private int count;
private final WorkerThread[] thraedPool;
public Channel(int thraedSize) {
this.requestQueue = new Request[MAX_REQUEST];
head = 0;
tail = 0;
count = 0;
thraedPool = new WorkerThread[thraedSize];
for (int i = 0; i < thraedPool.length; i++) {
thraedPool[i] = new WorkerThread("Worker-"+ i,this);
}
}
public void startWorker(){
for (int i = 0; i < thraedPool.length; i++) {
thraedPool[i].start();
}
}
public synchronized void putRequest(Request request){
while (count >= requestQueue.length){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
requestQueue[tail] = request;
tail = (tail+1)%requestQueue.length;
count++;
notifyAll();
}
public synchronized Request takeRequest(){
while (count <= 0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
Request request = requestQueue[head];
head = (head + 1)%requestQueue.length;
count--;
notifyAll();
return request;
}
}
R
e
questqueue可以当做桌子,是一个数量有限的请求队列。threadPool是一个工人线程的数组,这就是一个线程池。在这里提供了putRequest和takeRequest方法,分别是往请求队列放入请求和取出请,这里使用了上一篇博文讲到的生产者消费者模式 java多线程设计模式之消费者生产者模式。确保了WorkerThread和ClientThread之间可以友好合作。
工人线程:
public class WorkerThread extends Thread{
private final Channel channel;
public WorkerThread(String name,Channel channel) {
super(name);
this.channel = channel;
}
@Override
public void run() {
while (true){
Request request = channel.takeRequest();
request.execute();
}
}
}
这里
就是一个不断从请求队列中取出请求然后执行请求的过程,保证了工人线程的复用,并不会执行完一个请求任务就销毁。
最后是Main:
public class Main {
public static void main(String[] args){
Channel channel = new Channel(5);
channel.startWorker();
new ClientThread(channel,"A").start();
new ClientThread(channel,"B").start();
new ClientThread(channel,"C").start();
}
}
结果:
Worker-4executeRequest fromBNo.4
Worker-3executeRequest fromCNo.10
Worker-2executeRequest fromANo.6
Worker-1executeRequest fromBNo.5
Worker-0executeRequest fromCNo.11
Worker-4executeRequest fromCNo.12
Worker-2executeRequest fromANo.7
Worker-3executeRequest fromCNo.13
Worker-1executeRequest fromBNo.6
Worker-0executeRequest fromBNo.7
Worker-2executeRequest fromANo.8
Worker-3executeRequest fromCNo.15
Worker-4executeRequest fromCNo.14
可以看出线程执行任务的线程就是WorkerThread1,2,3,4,5五个,它们不断执行来自ClientThread A,B,C的请求任务。