线程池的基本思想
创建线程对象和清除线程垃圾都会大量占用CPU等系统资源,使用线程池就可以很好的解决资源浪费的问题。
线程池的基本思想:
在系统中开辟一块区域,存放一些待命的线程,这个区域成为线程池。
如果有需要执行的任务,则从线程池中借一个待命的线程来执行指定的任务,任务执行结束在将借的线程归还
这样就避免了大量创建线程对象,浪费CPU、内存资源的问题。
常用线程池类型
固定尺寸的线程池:待命线程的数量是一定,创建线程池后不能改变
优点:实现简单
缺点:
如果要执行的任务数量很多,超过待命线程的数量,有可能造成大量的等待
如果要执行的任务数量很少,大量待命的线程也可能造成内存的浪费
可变尺寸的线程池:待命线程的数量根据任务负载的需要动态变化
优点:
如果等待执行的任务数量很多,系统会自动扩充待命线程的数量,来提高效率
如果等待执行的任务数量很少,系统会自动缩减待命线程的数量,来减少内存消耗
缺点:实现复杂
固定尺寸线程池
创建线程池
获取固定线程池的方法,使用Executors类的静态工厂方法创建线程池:
public static ExecutorService newFixedThreadPool(int nThreads)
创建一个具有固定大小的线程池,如果线程异常终止,将产生新的线程来替代它
public static ExecutorService newSingleThreadExecutor()
创建一个线程池,在同一时刻只可以执行一个任务,使用这个方法可以保证多个任务顺序执行,并且不可以被重新配置为支持多个线程,如果该线程异常终止,将产生新的线程来替代它
两个方法返回的都是ExecutorService接口类型的引用,指向线程池对象,使用它的execute方法使用线程池中的线程执行指定的任务
void execute(Runnable command)
关闭线程池
使用线程池的程序在所有任务结束后并不会自动退出,因为线程池中的线程在执行完任务后并不死亡,而是等待执行新的任务,如果希望在程序在执行完所有任务后退出,需要调用ExecutorService接口中的shutdown方法来关闭线程池
void shutdown()
关闭线程池,不再接受新任务,旧任务结束则关闭线程池中所有的线程(等待所有任务,包括等待队列的任务都完成了才关闭线程池)
List<Runnable> shutdownNow()
无论所有任务是否执行结束,立即关闭线程池,List中为等待执行的任务
public class Main{
public static void main(String[] args){
MyTask task1 = new MyTask("task1", 50);
MyTask task2 = new MyTask("task2", 50);
MyTask task3 = new MyTask("task3", 30);
//创建固定大小的线程池
ExecutorService threadPool = Executors.newFixedThreadPool(2);
threadPool.execute(task1);
threadPool.execute(task2);
threadPool.execute(task3);
//所有任务都结束后关闭线程池
threadPool.shutdown();
}
}
class MyTask implements Runnable{
private int count;
private String taskName;
public MyTask(String taskName, int count){
this.count = count;
this.taskName = taskName;
}
public void run(){
System.out.println("\n"+Thread.currentThread().getName()+"开始执行任务"+taskName+">>");
for(int i=0;i<count;i++){
System.out.print(taskName+"-"+i+" ");
}
System.out.println("\n"+taskName+"任务执行结束。。");
}
}
运行结果:
pool-1-thread-1开始执行任务task1>>
pool-1-thread-2开始执行任务task2>>
task1-0 task2-0 task1-1 task2-1 task1-2 task2-2 task1-3 task2-3 task1-4 task2-4 task1-5 task2-5 task1-6 task2-6 task1-7 task2-7 task1-8 task2-8 task1-9 task2-9 task1-10 task1-11 task1-12 task1-13 task2-10 task1-14 task2-11 task1-15 task2-12 task1-16 task2-13 task1-17 task2-14 task1-18 task2-15 task1-19 task2-16 task1-20 task2-17 task1-21 task2-18 task1-22 task2-19 task1-23 task2-20 task1-24 task2-21 task1-25 task2-22 task2-23 task2-24 task2-25 task2-26 task2-27 task1-26 task2-28 task2-29 task2-30 task1-27 task2-31 task2-32 task1-28 task2-33 task2-34 task2-35 task2-36 task2-37 task2-38 task2-39 task2-40 task2-41 task2-42 task2-43 task2-44 task2-45 task2-46 task2-47 task2-48 task2-49
task2任务执行结束。。
task1-29 task1-30
pool-1-thread-2开始执行任务task3>>
task3-0 task3-1 task3-2 task3-3 task3-4 task3-5 task3-6 task3-7 task3-8 task3-9 task3-10 task3-11 task3-12 task3-13 task3-14 task3-15 task3-16 task3-17 task3-18 task3-19 task3-20 task3-21 task3-22 task3-23 task3-24 task3-25 task1-31 task3-26 task3-27 task3-28 task3-29
task3任务执行结束。。
task1-32 task1-33 task1-34 task1-35 task1-36 task1-37 task1-38 task1-39 task1-40 task1-41 task1-42 task1-43 task1-44 task1-45 task1-46 task1-47 task1-48 task1-49
task1任务执行结束。。
从结果中可以看出,线程池中的两个线程pool-1-thread-1、pool-1-thread-2分别执行了task1、task2,
pool-1-thread-2执行完task2后接着又执行了task3
实际开发中,线程池一般只有一个,用其中的线程完成各种任务。
如果需要执行的任务数量大于线程池的尺寸,则有的任务要进入此线程池的的等待队列
单任务线程池的使用
同一时刻只允许执行一个任务,而且要求任务按照请求的顺序执行
public class Main{
public static void main(String[] args){
MyTask task1 = new MyTask("task1", 20);
MyTask task2 = new MyTask("task2", 20);
MyTask task3 = new MyTask("task3", 10);
//创建单任务线程池
ExecutorService singlePool = Executors.newSingleThreadExecutor();
singlePool.execute(task1);
singlePool.execute(task2);
singlePool.execute(task3);
//所有任务都结束后关闭线程池
singlePool.shutdown();
}
}
class MyTask implements Runnable{
private int count;
private String taskName;
public MyTask(String taskName, int count){
this.count = count;
this.taskName = taskName;
}
public void run(){
System.out.println("\n"+Thread.currentThread().getName()+"开始执行任务"+taskName+">>");
for(int i=0;i<count;i++){
System.out.print(taskName+"-"+i+" ");
}
System.out.println("\n"+taskName+"任务执行结束。。");
}
}
运行结构:
pool-1-thread-1开始执行任务task1>>
task1-0 task1-1 task1-2 task1-3 task1-4 task1-5 task1-6 task1-7 task1-8 task1-9 task1-10 task1-11 task1-12 task1-13 task1-14 task1-15 task1-16 task1-17 task1-18 task1-19
task1任务执行结束。。
pool-1-thread-1开始执行任务task2>>
task2-0 task2-1 task2-2 task2-3 task2-4 task2-5 task2-6 task2-7 task2-8 task2-9 task2-10 task2-11 task2-12 task2-13 task2-14 task2-15 task2-16 task2-17 task2-18 task2-19
task2任务执行结束。。
pool-1-thread-1开始执行任务task3>>
task3-0 task3-1 task3-2 task3-3 task3-4 task3-5 task3-6 task3-7 task3-8 task3-9
task3任务执行结束。。
创建大小可变的线程池
public static ExecutorService newCachedThreadPool()
创建一个大小可变的线程池,当执行任务时先选取重用缓存已有的空闲线程来完成任务,如果没有空闲线程,则创建新线程
空闲超过60秒的线程将被从线程池中删除
...
public static void main(String[] args){
MyTask task1 = new MyTask("task1", 30);
MyTask task2 = new MyTask("task2", 30);
MyTask task3 = new MyTask("task3", 20);
//创建大小可变的线程池
ExecutorService threadPool = Executors.newCachedThreadPool();
threadPool.execute(task1);
threadPool.execute(task2);
threadPool.execute(task3);
//所有任务都结束后关闭线程池
threadPool.shutdown();
}
...
运行结果:
pool-1-thread-1开始执行任务task1 >>
task1-0 task1-1 task1-2
pool-1-thread-2开始执行任务task2 >>
task1-3 task1-4 task1-5 task1-6
pool-1-thread-3开始执行任务task3 >>
task3-0 task3-1 task3-2 task3-3 task3-4 task3-5 task3-6 task1-7 task3-7 task3-8 task3-9 task2-0 task3-10 task1-8 task3-11 task3-12 task3-13 task3-14 task3-15 task3-16 task3-17 task3-18 task2-1 task3-19 task1-9
task3任务执行结束 。。
task2-2 task2-3 task2-4 task2-5 task2-6 task2-7 task2-8 task2-9 task2-10 task2-11 task2-12 task2-13 task2-14 task2-15 task1-10 task2-16 task1-11 task2-17 task2-18 task2-19 task1-12 task2-20 task2-21 task1-13 task2-22 task1-14 task2-23 task1-15 task2-24 task1-16 task1-17 task1-18 task1-19 task1-20 task1-21 task1-22 task1-23 task2-25 task1-24 task2-26 task1-25 task2-27 task1-26 task2-28 task1-27 task2-29 task1-28
task2任务执行结束 。。
task1-29
task1任务执行结束 。。
三个任务交替执行,可变尺寸的线程池可以根据任务的多少来自动调整待命线程的数量,优化执行性能。
延迟线程池
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize)
corePoolSize,线程池中线程的数量
public static ScheduledExecutorService newSingleThreadScheduledExecutor()
创建一个延迟线程池,其中只有一个待命线程
以上两个方法返回的都是ScheduledExecutorService,是ExecutorService的子接口,特有的方法schedule来延迟执行指定的任务
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
//TimeUnit为枚举类型
...
public static void main(String[] args){
MyTask task1 = new MyTask("task1", 30);
MyTask task2 = new MyTask("task2", 30);
MyTask task3 = new MyTask("task3", 20);
//创建有时延的线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(2);
//创建单线程延时线程池
ScheduledExecutorService singleThreadPool = Executors.newSingleThreadScheduledExecutor();
threadPool.schedule(task1, 1, TimeUnit.SECONDS);
threadPool.schedule(task2, 1500, TimeUnit.MILLISECONDS);
singleThreadPool.schedule(task3, 2000, TimeUnit.MILLISECONDS);
//所有任务都结束后关闭线程池
threadPool.shutdown();
singleThreadPool.shutdown();
}
...
运行结果:
pool-1-thread-1开始执行任务task1 >>
task1-0 task1-1 task1-2 task1-3 task1-4 task1-5 task1-6 task1-7 task1-8 task1-9 task1-10 task1-11 task1-12 task1-13 task1-14 task1-15 task1-16 task1-17 task1-18 task1-19 task1-20 task1-21 task1-22 task1-23 task1-24 task1-25 task1-26 task1-27 task1-28 task1-29
task1任务执行结束 。。
pool-1-thread-2开始执行任务task2 >>
task2-0 task2-1 task2-2 task2-3 task2-4 task2-5 task2-6 task2-7 task2-8 task2-9 task2-10 task2-11 task2-12 task2-13 task2-14 task2-15 task2-16 task2-17 task2-18 task2-19 task2-20 task2-21 task2-22 task2-23 task2-24 task2-25 task2-26 task2-27 task2-28 task2-29
task2任务执行结束 。。
pool-2-thread-1开始执行任务task3 >>
task3-0 task3-1 task3-2 task3-3 task3-4 task3-5 task3-6 task3-7 task3-8 task3-9 task3-10 task3-11 task3-12 task3-13 task3-14 task3-15 task3-16 task3-17 task3-18 task3-19
task3任务执行结束 。。
自定义参数的线程池
使用ThreadPoolExecutor类(实现了ExecutorService)来实现自定义的线程池,当有新任务到达时,按照以下规则处理:
1)如果当前线程池中的线程数量比规定标准值少,则倾向于创建线程;
2)如果当前线程池中的线程数量比规定标准值多,则倾向于把新的任务放到队列中;如果队列已满,并且线程数量没有超过最大值,则创建新线程。
3)如果当前线程池中的线程数量已达最大值,且队列已满,则请求被拒绝。
4)如果空闲线程超过预设的存活时间,则将空闲线程对象销毁。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue)
ThreadPoolExecutor类一些常用的方法:
public int getCorePoolSize() 获取线程池的标准大小
public int getActiveCount() 返回线程池中正在执行任务的线程数量
public int getPoolSize() 获取线程池的当前大小
public int getMaximumPoolSize() 获取线程池的最大大小
public BlockingQueue<Runnable> getQueue() 返回线程池的工作等待队列
...
public static void main(String[] args){
MyTask task1 = new MyTask("task1", 30);
MyTask task2 = new MyTask("task2", 30);
MyTask task3 = new MyTask("task3", 20);
MyTask task4 = new MyTask("task4", 20);
//创建工作等待队列
BlockingQueue workQueue = new ArrayBlockingQueue(3);
//创建自定义线程池
ThreadPoolExecutor myThreadPool = new ThreadPoolExecutor(
2, 4, 100, TimeUnit.SECONDS, workQueue);
myThreadPool.execute(task1);
myThreadPool.execute(task2);
myThreadPool.execute(task3);
myThreadPool.execute(task4);
//所有任务都结束后关闭线程池
myThreadPool.shutdown();
}
...
运行结果:
pool-1-thread-1开始执行任务task1 >>
task1-0 task1-1 task1-2 task1-3 task1-4
pool-1-thread-2开始执行任务task2 >>
线程池的当前线程数:2
task2-0 task2-1 task2-2 task2-3 task2-4 task2-5 task2-6 task2-7 task2-8 task2-9 task2-10 task2-11 task2-12 task2-13 task2-14 task2-15 task2-16 task2-17 task2-18 task2-19 task2-20 task1-5 task2-21 task1-6 task2-22 task1-7 task2-23 task1-8 task2-24 task1-9 task2-25 task1-10 task2-26 task1-11 task2-27 task1-12 task2-28 task2-29 task1-13
task2任务执行结束 。。
task1-14 task1-15
pool-1-thread-2开始执行任务task3 >>
task1-16 task3-0 task1-17 task3-1 task3-2 task3-3 task1-18 task3-4 task1-19 task3-5 task1-20 task3-6 task1-21 task3-7 task1-22 task3-8 task1-23 task3-9 task1-24 task3-10 task1-25 task3-11 task1-26 task3-12 task1-27 task3-13 task1-28 task3-14 task1-29 task3-15
task1任务执行结束 。。
task3-16
pool-1-thread-1开始执行任务task4 >>
task3-17 task4-0 task3-18 task4-1 task3-19 task4-2
task3任务执行结束 。。
task4-3 task4-4 task4-5 task4-6 task4-7 task4-8 task4-9 task4-10 task4-11 task4-12 task4-13 task4-14 task4-15 task4-16 task4-17 task4-18 task4-19
task4任务执行结束 。。