为什么使用线程池
无限制线程缺陷
多线程可以发挥多核处理器的计算能力,提高系统的吞吐量和性能,但是若不控制和管理的随意使用多线程,对系统性能反而产生不利影响。
简单的线程创建:
new Thread(new Runnable() {
@Override
public void run() {
}
}).start();
简单的系统中,上述代码并无问题,但是在正式生产环境系统中,若开启过多的线程数量,会耗尽CPU和内存资源。
为什么不能无限制创建线程?
- 线程的创建和关闭需要花费时间,如果每一个小任务都创建一个线程,可能出现创建和销毁线程花费的时间大于真实工作时间消耗,。
- 线程本身也是占用内存资源的,处理不当可能会导致内存溢出。大量线程回收也会给GC带来压力
简单的线程池实现
为了节省系统在多线程并发时不断创建和销毁线程所带来的的额外开销,需要引入线程池。线程池的基本功用是线程的复用。当系统提交一个任务需要一个线程,先去线程池查找是否有空余的线程,有则直接使用,无则创建新的线程。任务完成后,将线程放入线程池的空闲队列,等待下次使用。
这样,在线程频繁调度的场景,节约了线程频繁创建和销毁的开销。
线程池实现:
import java.util.List;
import java.util.Vector;
public class ThreadPool {
private static ThreadPool instance = null;
//空闲的线程队列
private List<PThread> idleThreads;
//已有的线程总数
private int threadCounter;
private boolean isShutDown = false;
private ThreadPool(){
this.idleThreads = new Vector<>(5);
threadCounter = 0;
}
//取得线程池实例
public synchronized static ThreadPool getInstance(){
if(instance == null){
instance = new ThreadPool();
}
return instance;
}
//将线程放入池中
public synchronized void repool(PThread repoolingThread){
if(!isShutDown){
idleThreads.add(repoolingThread);
}else {
repoolingThread.shutdown();
}
}
public synchronized void shutdown(){
isShutDown = true;
for (int threadIndex = 0; threadIndex < idleThreads.size();threadIndex++){
PThread idleThread = idleThreads.get(threadIndex);
idleThread.shutdown();
}
}
//执行任务
public synchronized void start(Runnable target){
PThread thread = null;
//如果有空闲线程,则直接使用
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();
}
}
}
//PThread线程主体部分是一个无限循环,该线程在手动关闭前永不结束
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;
}
@Override
public void run() {
//只要没有关闭,则一直不结束该线程
while (!isShutdown){
isIdle = false;
if(target != null){
//运行任务
target.run();
}
isIdle = true;
try {
//任务结束,不关闭线程,而是放入线程池空闲队列
pool.repool(this);
synchronized (this){
wait();
}
}catch (InterruptedException e){
}
isIdle = false;
}
}
public synchronized void setTarget(Runnable target) {
this.target = target;
//设置任务后,通知run方法,执行这个任务
notifyAll();
}
//关闭线程
public synchronized void shutdown(){
isShutdown = true;
notifyAll();;
}
}
不使用线程池和使用线程池性能对比:
public class MyThread extends Thread {
private String name;
public MyThread(){
}
public MyThread(String name){
this.name = name;
}
@Override
public void run() {
try {
Thread.sleep(100);
} catch (InterruptedException e){
}
}
}
不使用线程池:
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
new Thread(new MyThread("testNoThreadPool" + i)).start();
}
System.out.println("cost time:" + (System.currentTimeMillis() - start));
Output:
cost time:317
使用线程池
long start = System.currentTimeMillis();
for (int i = 0; i < 1000; i++) {
ThreadPool.getInstance().start(new MyThread("testNoThreadPool" + i));
}
System.out.println("cost time:" + (System.currentTimeMillis() - start));
Output:
cost time:220