详解 线程池
(请观看本人博文 —— 《详解 多线程》)
线程池
概念:
装有一定线程对象的容器。
程序启动一个新线程成本是比较高的,因为它涉及到要与操作系统进行交互。
而使用线程池可以很好的提高性能,尤其是当程序中要创建大量生存期很短的线程时,更应该考虑使用线程池。
线程池里的每一个线程代码结束后,并不会死亡,而是再次回到线程池中成为空闲状态,等待下一个对象来使用。
在JDK5之前,我们必须手动实现自己的线程池,
从JDK5开始,Java内置支持线程池的类 —— ExecutorService 类
此外,线程池有两大优点:
优点:
- 线程是稀缺资源,使用线程池可以减少创建和销毁线程的次数,每个工作线程都可以重复使用
- 可以根据系统的承受能力,调整线程池中工作线程的数量,防止因为消耗过多内存导致服务器崩溃
那么,现在,本人来讲解下线程池的底层实现原理:
底层实现原理:
流程:
提交一个任务到线程池中,线程池的处理流程如下:
- 判断线程池里的核心线程是否都在执行任务:
如果不是(核心线程空闲或者还有核心线程没有被创建)则创建一个新的工作线程来执行任务;
如果是(核心线程都在执行任务),则进入下个流程。- 判断线程池的工作队列是否已满:
如果工作队列没有满,则将新提交的任务存储在这个工作队列里;
如果工作队列满了,则进入下个流程。- 判断线程池里的线程是否都处于工作状态:
如果没有满,则创建一个新的工作线程来执行任务;
如果已经满了,则交给饱和策略来处理这个任务。
如下图:
那么,现在,本人来讲解下 上文所提及的 饱和策略:
饱和策略:
当队列和线程池都满了,说明线程池处于饱和状态,
那么必须对新提交的任务采用一种特殊的策略来进行处理。
这个策略默认配置是AbortPolicy,表示无法处理新的任务而抛出异常。
对于上述情况,Java提供了4中策略:
- AbortPolicy:直接抛出异常
- CallerRunsPolicy:只用调用所在的线程运行任务
- DiscardOldestPolicy:丢弃队列里最近的一个任务,并执行当前任务。
- DiscardPolicy:不处理,丢弃掉
那么,接下来,本人就来讲解下与线程池相关的两个类吧:
我们想要获得 ExecutorService 类 的对象,就只能通过 调用Executors 工厂类的方法来获取,所以,本人先来简单介绍下 Executors 工厂类:
Executors 工厂类:
对于 Executors 工厂类,我们只需了解这个类是如何产生 ExecutorService类的对象即可。
所以,本人先来介绍下这个类的常用API:
常用API:
- public static ExecutorService newCachedThreadPool():
根据任务的数量来创建线程对应的线程个数- public static ExecutorService newFixedThreadPool(int nThreads):
固定初始化几个线程- public static ExecutorService newSingleThreadExecutor():
初始化一个线程的线程池
那么,现在,本人来介绍下 ExecutorService 类:
ExecutorService 类:
对象的获得方法,在上面本人已经讲解过了。
那么,现在,本人来展示下这个类的常用API:
常用API:
- < T > Future< T > submit(Callable< T > task)
提交一个值返回任务执行,并返回一个表示任务挂起结果的未来- Future<?> submit(Runnable task)
提交执行一个Runnable任务并返回一个表示该任务的未来- < T > Future< T > submit(Runnable task, T result)
提交执行一个Runnable任务并返回一个表示该任务的未来- boolean isShutdown()
返回 true如果执行器已关闭- boolean isTerminated()
返回 true如果所有任务都完成后,关闭- void shutdown()
启动一个有序的关机,在以前提交的任务被执行,但没有新的任务将被接受- List< Runnable > shutdownNow()
试图阻止所有积极执行任务,停止等待任务的处理,并返回一个列表,正在等待执行的任务- boolean awaitTermination(long timeout, TimeUnit unit)
直到所有的任务都完成后,关闭请求,或超时发生,或当前线程被中断,以先发生的情况- < T > List<Future< T >> invokeAll(Collection<? extends Callable< T >> tasks)
执行给定的任务,返回一个未来持有他们的状态和结果的列表时,所有的完整- < T > List<Future< T >> invokeAll(Collection<? extends Callable< T >> tasks, long timeout, TimeUnit unit)
执行给定的任务,返回一个未来持有他们的状态和结果的列表时,所有的完成或超时到期,以先发生的- < T > T invokeAny(Collection<? extends Callable< T >> tasks)
执行给定的任务,返回已成功完成的结果(即,不抛出一个例外),如果任何- < T > T invokeAny(Collection<? extends Callable< T >> tasks, long timeout, TimeUnit unit)
执行给定的任务,返回一个成功完成的结果(即,不抛出异常),如果做了超时之前经过
对于上述的API ,本人现在来做几点说明:
- 每次我们使用完后,一定要记得调用shutdown()方法,
否则线程不会关闭、程序不会停止运行- 只需将线程作为submit()的参数传入,就会有线程池中的线程来为完成该“任务”
现在,本人来通过几个例子,来展示下这个类的基本使用:
本人首先来给出一个 Runnable的实现类 和 一个Callable的实现类:
package edu.youzg.about_synchronized.core;
public class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"任务执行了");
}
}
package edu.youzg.about_thread.core;
import java.util.concurrent.Callable;
import static java.lang.Thread.sleep;
public class MyCallable implements Callable<Integer> {
int num;
public MyCallable(int num) {
this.num=num;
}
@Override
public Integer call() throws Exception {
int sum=0;
for (int i = 1; i <=num; i++) {
sum += i;
}
return sum;
}
}
例1 —— 使用无参构造获取线程池:
package edu.youzg.about_thread.core;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args){
ExecutorService executorService = Executors.newCachedThreadPool(); //通过无参构造出来的线程池,容量是随着任务的数量改变的
MyRunnable myRunnable = new MyRunnable();
executorService.submit(myRunnable);
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
//关闭线程池
executorService.shutdown();
}
}
本人现在来展示下运行结果:
可以看到:我们提交多少个“任务”,线程池中就会提供多少个线程来处理这些“任务”
例2 —— 使用有参构造获取线程池:
package edu.youzg.about_thread.core;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args){
//这个线程里面,提前创建三个线程对象
ExecutorService executorService = Executors.newFixedThreadPool(3);
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
//关闭线程池
executorService.shutdown();
}
}
本人来展示下运行结果:
可以看到:线程池中只创建了三个线程,随机来完成传入的“任务”
例3 ——
package edu.youzg.about_thread.core;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class Test {
public static void main(String[] args){
//这个线程池里面,只有一个线程对象
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.submit(new MyRunnable());
executorService.shutdown();
}
}
现在,本人来展示下运行结果:
能够看到,线程池中只创建了一个线程。
例4 —— 提交Callable实现类的任务作为submit()的参数:
package edu.youzg.about_thread.core;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class Test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ExecutorService executorService = Executors.newFixedThreadPool(3);
MyCallable myCallable = new MyCallable(10);
Future<Integer> f = executorService.submit(myCallable);
Integer integer = f.get();
System.out.println(integer);
MyCallable myCallable2 = new MyCallable(100);
Future<Integer> f2 = executorService.submit(myCallable2);
Integer integer1 = f2.get(); //获取线程执行完之后的返回结果
System.out.println(integer1);
executorService.shutdown();
}
}
现在,本人再来展示下运行结果:
可以看到:
若传入的参数若是Runnable线程,就会运行完run()中的内容;
若传入的参数若是Callable线程,不仅就会运行完run()中的内容,还可以获取返回值。
我们所传的线程参数,根据我们的需要来传入即可。
基本实现:
请观看本人博文 —— 《详解 线程池 的基本实现》
(本人 《详解 多线程》 博文 链接:https:////www.cnblogs.com/codderYouzg/p/12418935.html)