目录:
1、使用callable创建线程
2、使用ExectorService、callable、Future实现有返回结果的线程
3、两种基础的线程池
1)ThreadPollExecutor
2)ScheduledThreadPoolExecutor
4、线程池的好处
5、线程池的管理过程:
6、类层次结构
7、ThreadPoolExecutor带七个参数的构造方法
8、RejectedExecutionHandler handler拒绝任务处理器
9、 使用 Executors 创建几种常见的线程池
线程池:
线程是一个操作系统概念,操作系统负责这个线程的创建、挂起、运行、阻塞和终结操作,每个用户都来访问,可能就是1s的事,但是服务器要为你单独创建线程,那么物理机基本上都创建线程而业务请求处理资源少了
1、使用callable创建线程
public class Test1 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
MyThread th=new MyThread();
FutureTask<String> future=new FutureTask<>(th);
Thread t=new Thread(future);
t.start();
String sum=future.get(); //这个方法会阻塞,一直等到线程执行完
System.out.println(sum);
System.out.println("-------main结束 ------");
}
}
class MyThread implements Callable<String>{
public String call() throws Exception {
int sum=0;
for(int i=0;i<=100;i++) {
sum+=i;
System.out.println("进行到第" +i +"次计算");
}
return "计算结果:"+sum;
}
}
call()方法:1)call()方法有返回值
2)可以抛出异常
3)可以声明泛型
class MyThread implements callable<String>{}
怎么实现? (1)MyThread th=new MyThread();
(2)FutureTask<String> future=new FuturnTask<>(th);//返回实现类的返回值future.get()
(3)Thread t=new Thread(future);
(4)t.start();
2、使用ExectorService、callable、Future实现有返回结果的线程
1)它们三个都属于Executor框架
如果想得到线程的返回值,则必须实现callable接口,可获取一个Future对象,然后用
Future.get()方法获取值
#get方法会阻塞(如果没有获取到返回值的话)
2)创建线程池
ExecutorService threadPool=Executors.newFixedThreadPool(5);//5代表线程池的核心线程数
threadPool.submit(new MyThread());//返回值为 Future
newFixedThreadPool的核心线程数和最大线程数相同
//例子 提交一个线程到线程池中去执行
public class Test2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService threadPool= Executors.newFixedThreadPool(5);
Future<Object> future= threadPool.submit(new MyThread());
System.out.println(future.get());
}
}
class MyThread implements Callable<Object>{
public Object call() throws Exception {
String name=Thread.currentThread().getName();
System.out.println(name+"开始");
int time=new Random().nextInt(3000);
Thread.sleep(time);
System.out.println(name+"结束");
return name+"执行了"+time +" ms";
}
}
//例子 提交10个线程到线程池中执行
public class Test2 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService threadPool= Executors.newFixedThreadPool(5); //线程池核心线程数是5
List<Future<Object>> futureList=new ArrayList<>();
for(int i=0;i<10;i++) {
Future<Object> future= threadPool.submit(new MyThread());
futureList.add(future);
}
threadPool.shutdown(); //说明不在再线程池中提交了
for(Future<Object> f:futureList) {
System.out.println(f.get());
}
}
}
class MyThread implements Callable<Object>{
public Object call() throws Exception {
String name=Thread.currentThread().getName();
System.out.println(name+"开始");
int time=new Random().nextInt(3000);
Thread.sleep(time);
System.out.println(name+"结束");
return name+"执行了"+time +" ms";
}
}
//例子 把普通的实现Runnable 接口的线程交给线程池去处理
public class Test3 {
public static void main(String[] args) {
ExecutorService pool=Executors.newCachedThreadPool();
for(int i=0;i<5;i++) {
pool.submit(new SaleThread());
}
pool.shutdown();
}
}
class SaleThread implements Runnable{
public void run() {
while(true) {
System.out.println("线程"+Thread.currentThread().getName()+" 正在运行 ");
}
}
}
3、两种基础的线程池
1)ThreadPollExecutor
2)ScheduledThreadPoolExecutor
上面两种方式都实现了ExecutorService接口
4、线程池的好处
1)降低资源的消耗,通过重复利用它创建的线程降低线程创建和销毁造成的消耗
2)提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行
3)提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控
5、线程池的管理过程:
corepoolsize:核心线程数
ExecutorService threadPool=Executors.newFixedThreadPool(5);
首先创建一个线程,提交任务到线程池中,直至任务数量增大到核心线程数,再有下一个线程的时候,当他判断的时候发现核心线程数已经满了,然后他就往任务队列里面放置,下面再来的任务都因为核心线程数满了,就放置在任务队列中,直至任务队列也饱满,此时还在来任务,他发现核心线程数和任务队列都是满的,然后他去判断有没有达到最大线程数,发现没有,就去执行任务,此时,后面再来的任务发现核心线程在忙,任务队列也满了,执行的任务也达到了最大线程数,此时再来的线程就会执行拒绝策略,需要handler来处理了,或者丢弃新任务,或者拒绝新任务,或者挤占掉已有的任务。在任务队列和线程池都饱和的情况下,一旦有线程处于等待(任务处理完毕,没有新任务)状态的时间超过keepAliveTime,则该线程终止,也就是说池中的线程数量会逐渐降低
6、类层次结构
(1)Executor 接口
(2)ExecutorService 接口,继承自Executor
(3)ThreadPoolExecutor 继承自ExecutorService,实际创建线程池就是创建这个类的对象
(4)ScheduledThreadPoolExecutor 继承自ThreadPoolExecutor,可以创建带有任务调度的线程池
(5)Executors 是个工具类,可以帮我们创建各种不同类型的线程池
1)Executor 接口
public interface Executor{
void execute(Runnable command);
}
2)ExecutorService
public interface ExecutorService extends Executor {
void shutdown(); //shutdown,执行后不再接收新任务,如果里面有任务,就执行完
List<Runnable> shutdownNow(); //立即停
boolean isShutdown();
boolean isTerminated();
boolean awaitTermination(long timeout, TimeUnit unit) //等多久后发出中断
throws InterruptedException;
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
<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) ..;
}
3) ThreadPoolExecutor 我们可以认为它的类对象就是线程池
这是它带七个参数的构造方法
public ThreadPoolExecutor(
int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null : AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
7、ThreadPoolExecutor带七个参数的构造方法***********************重要***************
public ThreadPoolExecutor(
int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程的最大生命周期
TimeUnit unit,//时间计量单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//定义如何启动一个线程,可以设置线程的名称,并且可以确定是否是后台线程
RejectedExecutionHandler handler){//拒绝策略
.......
}
详细解说线程池的七个参数:
1)corePoolSize 核心线程数
核心线程:线程池新建线程的时候,如果当前线程总数小于corePoolSize,则新建的是核心线程,如果超过corepoolsize,则新建的是非核心线程,核心线程默认情况下会一直存活在线程池中,即使这个线程啥也不干(闲置状态),如果指定 ThreadPoolExecutor的allowCoreThreadTimeOut这个属性为true,那么核心线程处于闲置状态超过一定事件(时长由下面参数决定),就会被销毁掉
2)maximumPoolSize 线程池中的最大线程数
最大线程数=核心线程数+非核心线程数
3)keepAliveTime 线程的最大生命周期
一个非核心线程,如果处于闲置状态的时长超过这个参数锁设定的时长,就会被销毁掉,
如果将参数allowcorethreadtimeout=true,则这个线程的最大生命周期设置的超时时间也会作用于核心线程
4)TimeUnit unit 时间计量单位,是一个枚举类型(enumerate)
NANOSECONDS : 1微毫秒 = 1微秒 / 1000
MICROSECONDS : 1微秒 = 1毫秒 / 1000
MILLISECONDS : 1毫秒 = 1秒 /1000
SECONDS : 秒
MINUTES : 分
HOURS : 小时
DAYS : 天
传的时候,可以:TimeUnit.SECONDS 这样写
5)BlockingQueue<Runnable> workQueue
线程池中的任务队列,维护者等待执行的Runnable对象任务队列。当线程池中的线程都处于运行状态,而此时任务数量继续增加,则需要一个容器来容纳这些任务,这就是任务队列。这个任务队列是一个阻塞式的单端队列
常用的workqueue类型
###synchronousQueue(同步队列)
这个队列接收到新的任务的时候,会直接提交给线程处理,而不保留它,如果所有的线程都在工作怎么办?那就新建一个线程来处理这个这个任务!所以为了保证不出现 <线程数达到maximumPoolSize而不能新建线程>的错误, 使用这个类型队列的时候,maximumPoolSize一般指定成Integer.MAX_VALUE,即无限大
###LinkedBlockingQueue队列
这个队列接收到任务的时候,如果当前线程小于核心线程数,则新建线程处理任务,,如果当前线程数等于核心线程数,则进入等待队列等待。由于这个队列没有最大值限制,即所有超过核心线程数的任务都会被加入到队列中,这也就导致了maximumPoolSize的设定失效,因为总线程数永远不会超过核心线程数
###ArrayBlockingQueue
这个队列接到新任务时,如果当前任务没有达到核心线程的数量,则新建核心线程执行任务,如果达到了核心线程,则入队等候,如果队列已满,则新建非核心线程执行任务,如果总线程数量达到了最大线程数,并且队列也已满,则执行拒绝策略
###DelayQueue
队列内的元素必须实现Delay接口,这就意味着你传进去的任务必须先实现Delay接口,这个队列接到新的任务时,首先先入队,只有达到了指定的延时时间,才会去执行任务
8、RejectedExecutionHandler handler拒绝任务处理器
由于超过线程容量和队列容量而对继续增加的任务进行处理的程序
jdk内置了四种拒绝策略
1)AbortPolicy 中止策略
为java的默认策略,不执行此任务,而是直接抛出一个运行时异常
切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出。
2)CallerRunsPolicy
在任务被拒绝添加后,会在调用execute()方法 ( 或submit() )的线程来执行被拒绝的任务。 除非executor被关闭,否则任务不会被丢弃。显然这样不会真的丢弃任务,但是, 调用者线 程性能可能急剧下降。
在调用execute的线程里面执行此command,会阻塞入口
3)DiscardOldestPolicy策略
从队列里面抛弃head的一个任务,并再次execute 此task
4)DiscardPolicy策略
直接抛弃,任务不执行,空方法。
9、 使用 Executors 创建几种常见的线程池
它是一个工具类, 可以帮我们创建不同类型的线程池
1.可缓存线程池 CachedThreadPool()
调用 ExecutorService pool= Executors.CachedThreadPool();
源码:
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
根据源码可以看出:
(1).这种线程池内部没有核心线程,线程的数量是有没限制的。
(2).在创建任务时,若有空闲的线程时则复用空闲的线程,若没有则新建线程。
(3).没有工作的线程(闲置状态)在超过了60S还不做事,就会销毁。
2.FixedThreadPool 定长线程池
调用:ExecutorService pool= Executors.FixedThreadPool();
源码:
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads, //最大线程数等于核心线程数
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
根据源码可以看出:
该线程池的最大线程数等于核心线程数,所以在默认情况下,该线程池的线程不会因为闲 置状态超时而被销毁。
这个实例会复用 固定数量的线程 处理一个 共享的无边界队列 。任何时间点,最多有 nThreads 个线程会处于活动状态执行任务。
如果当所有线程都是活动时, 有多的任务被提交过来, 那么它会一致在队列中等待直到有线程可用。如果任何线程在执行过程中因为错误而中止,新的线程会替代它的位置来执行后续的任务。所有线程都会一致存于线程池中,直到显式的执行 ExecutorService.shutdown() 关闭。
3.SingleThreadPool (可以让线程顺次执行)
调用 ExecutorService pool = Executors.newSingleThreadExecutor();
源码:
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService (
new ThreadPoolExecutor(1, 1, 0L,
TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()
)
);
}
、根据源码可以看出:
核心线程数是 1 , 最大线程数 1
(1).有且仅有一个工作线程执行任务
(2).所有任务按照指定顺序执行,即遵循队列的入队出队规则
4.ScheduledThreadPool 可以进行延迟执行和周期性重复执行
调用 ExecutorService pool = Executors.newScheduledThreadPool(int corePoolSize));
源码:
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize{
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE,
DEFAULT_KEEPALIVE_MILLIS, MILLISECONDS,
new DelayedWorkQueue());
}
DEFAULT_KEEPALIVE_MILLIS就是默认10L,这里就是10秒。这个线程池有点像是 CachedThreadPool和FixedThreadPool 结合了一下。
1.不仅设置了核心线程数,最大线程数也是 Integer.MAX_VALUE。
2.这个线程池是上述4个中为唯一个有延迟执行和周期执行任务的线程池。
//表示在3秒之后开始执行我们的任务。
mScheduledThreadPool.schedule(new Runnable() {
public void run() {
//....
}
}, 3, TimeUnit.SECONDS);
//延迟3秒后执行任务,从开始执行任务这个时候开始计时,每7秒执行一次不管执行任务需要多长的时间。
mScheduledThreadPool.scheduleAtFixedRate(new Runnable() {
public void run() {
//....
}
},3, 7, TimeUnit.SECONDS);
/**延迟3秒后执行任务,从任务完成时这个时候开始计时,7秒后再执行,
*再等完成后计时7秒再执行也就是说这里的循环执行任务的时间点是
*从上一个任务完成的时候。
*/
mScheduledThreadPool.scheduleWithFixedDelay(new Runnable() {
public void run() {
//....
}
},3, 7, TimeUnit.SECONDS);
//例子 scheduleWithFixedDelay
public class Test5 {
public static void main(String[] args) {
ScheduledExecutorService pool=Executors.newScheduledThreadPool(5);
for(int i=0;i<10;i++) {
//pool.submit(new MyWorker());
//参数说明:
//3 表示3秒以后开始运行
//7 表示线程执行完任务以后,再过7秒,再循环进行
//TimeUnit.SECONDS 表示以秒为单位
pool.scheduleWithFixedDelay(new MyWorker(), 3, 7, TimeUnit.SECONDS);
}
}
}
class MyWorker implements Runnable{
public void run() {
int time=new Random().nextInt(3000);
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"工作了 "+time +" ms");
}
}
用它指行一般任务和前面的线程池用法大致相同,我们主要关心它用于延时执行任务和周期执行任务的用法
线程池创建示例
public class Test6 {
public static void main(String[] args) throws InterruptedException, ExecutionException {
//核心线程数
int corePoolSize=5;
//最大线程数
int maxinumPoolSize=7;
//超时时间
int keepAliveTime=60;
//时间单位
TimeUnit timeUnit=TimeUnit.SECONDS;
//线和池的缓存队列
BlockingQueue<Runnable> workQueue=new ArrayBlockingQueue<>(10);
//工厂
ThreadFactory factory=Executors.defaultThreadFactory();
//拒绝策略
RejectedExecutionHandler myHandler=new RejectedExecutionHandler() {
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println(r+" 被丢弃了");
}
};
ExecutorService pool=new ThreadPoolExecutor(corePoolSize,maxinumPoolSize,keepAliveTime,timeUnit,workQueue,factory,myHandler);
List<Future <Integer> > futureList=new ArrayList<>();
for(int i=0;i<20;i++) {
Future <Integer> future=pool.submit(new Sporter());
futureList.add(future);
}
pool.shutdown();
for(Future <Integer> f: futureList) {
System.out.println(f.get());
}
}
}
class Sporter implements Callable<Integer>{
public Integer call() throws Exception {
String name=Thread.currentThread().getName();
System.out.println(name +"开始");
int time=new Random().nextInt(3000);
System.out.println(name +"结束,执行了" +time +" ms");
return time;
}
}