如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。那么如果一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务的话,那么就可以大大提高其运行的效率。
在Java中可以通过线程池来达到这样的效果
1)了解java.util.concurrent包中的一些类和接口
ThreadPoolExecutor、AbstractExecutorService、ExecutorService和Executor
Executor是一个顶层接口,在它里面只声明了一个方法execute(Runnable),返回值为void,参数为Runnable类型,该方法就是用来执行传进去的任务的
ExecutorService接口继承了Executor接口,并声明了一些方法:submit、invokeAll、invokeAny以及shutDown等;
抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService中声明的所有方法
ThreadPoolExecutor继承了类AbstractExecutorService,并进行了扩展。ThreadPoolExecutor就是JKD中提供的一个线程池类。
ThreadPoolExecutor类中的一个构造器:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
参数的含义:
corePoolSize:线程池维护线程的最少数量
maximumPoolSize:线程池维护线程的最大数量
keepAliveTime:线程池维护线程所允许的空闲时间
unit:线程池维护线程所允许的空闲时间的单位
workQueue:线程池所使用的缓冲队列
例如:
public class ThreadPoolExecutorTest {
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 2000, TimeUnit.MILLISECONDS,
new ArrayBlockingQueue<Runnable>(5));
for(int i=0;i<15;i++){
MyRun myTask = new MyRun();
executor.execute(myTask);
System.out.println("线程池中线程数目: "+executor.getPoolSize()+
",队列中等待执行的任务数目: "+executor.getQueue().size()+
",已执行完的任务数目: "+executor.getCompletedTaskCount());
}
executor.shutdown();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(executor.getCompletedTaskCount());
}
}
class MyRun implements Runnable{
public void run() {
for(int i=0;i<100;i++){
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("任务结束");
}
}
注:从执行结果可以看出,当线程池中线程的数目大于5时,便将任务放入任务缓存队列里面,当任务缓存队列满了之后,便创建新的线程。
2)java.util.concurrent.Executors类
这个是一个工厂类,可以便捷的帮我们生产出线程池对象,同时该类中也提供了一些实用的工具方法.
工厂方法1:创建一个定长的线程池,可控制线程最大并发数,超出的线程会在队列中等待
public static ExecutorService newFixedThreadPool(int nThreads){
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
例如:
main:
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
for(int i = 0; i < 10; i++) {
final int index = i;
fixedThreadPool.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
fixedThreadPool.shutdown();
工厂方法2:创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,否则则新建线程
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
例如:
main:
ThreadPoolExecutor cachedThreadPool = (ThreadPoolExecutor) Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int index = i;
try {
Thread.sleep(index * 1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
cachedThreadPool.execute(new Runnable() {
public void run() {
System.out.println(index);
}
});
System.out.println("线程池中当前线程的数量: "+cachedThreadPool.getPoolSize());
}
cachedThreadPool.shutdown();
工厂方法3:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
例如:
main:
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int index = i;
singleThreadExecutor.execute(new Runnable() {
public void run() {
try {
System.out.println(index);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
singleThreadExecutor.shutdown();
工厂方法4:创建一个定长线程池,支持定时及周期性任务执行
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
例如:
main:
ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
scheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
System.out.println("延迟10秒后执行,之后再按照每3秒执行一次");
}
}, 10, 3, TimeUnit.SECONDS);