线程池的作用
1、减少线程创建与切换的开销
- 在没有使用线程池的时候,来了一个任务,就创建一个线程,我们知道系统创建和销毁工作线程的开销很大,而且频繁的创建线程也就意味着需要进行频繁的线程切换,这都是一笔很大的开销。
2、控制线程的数量
- 使用线程池我们可以有效地控制线程的数量,当系统中存在大量并发线程时,会导致系统性能剧烈下降。
3、循环利用有限的线程
- 线程池中会预先创建一些空闲的线程,他们不断的从工作队列中取出任务,然后执行,执行完之后会继续执行工作队列中的下一个任务,减少了创建和销毁线程的次数,每个线程都可以一直被重用,节省创建和销毁的开销。
线程池的使用
常用Java线程池本质上都是由ThreadPoolExecutor或者ForkJoinPool生成的,只是其根据构造函数传入不同的实参来实例化相应线程池而已。
(1)Executors是一个线程池工厂类,该工厂类包含如下集合静态工厂方法来创建线程池:
- newFixedThreadPool():创建一个任务固定大小、任务队列无界限、可重用的、具有固定线程数的线程池(oom)
- newSingleThreadExecutor():创建只有一个线程的线程池
- newCachedThreadPool():创建一个具有缓存功能的线程池
- newScheduledThreadPool():创建具有指定线程数的线程池,它可以在指定延迟后执行任务线程
- newWorkStealingPool():创建持有足够线程的线程池来支持给定的并行级别的线程池
(2)ExecutorService接口 Java线程池也采用了面向接口编程的思想,可以看到ThreadPoolExecutor和ForkJoinPool所有都是ExecutorService接口的实现类,在ExecutorService接口中定义了一些常用的方法,然后再各种线程池中都可以使用ExecutorService接口中定义的方法,常用的方法有如下几个:
向线程池提交线程
- Future<?> submit():将一个Runnable对象交给指定的线程池,线程池将在有空闲线程时执行Runnable对象代表的任务,该方法既能接收Runnable对象也能接收Callable对象,这就意味着sumbit()方法可以有返回值。
- void execute(Runnable command):只能接收Runnable对象,意味着该方法没有返回值。
关闭线程池
- void shutdown():阻止新来的任务提交,对已经提交了的任务不会产生任何影响。(等待所有的线程执行完毕才关闭)
- List<Runnable> shutdownNow(): 阻止新来的任务提交,同时会中断当前正在运行的线程,另外它还将workQueue中的任务给移除,并将这些任务添加到列表中进行返回。(立马关闭)
检查线程池状态
- boolean isShutdown():调用shutdown()或shutdownNow()方法后返回为true
- boolean isTerminated():当调用shutdown()方法后,并且所有提交的任务完成后返回为true;当调用shutdownNow()方法后,成功停止后返回为true。
常见线程池使用示例
(1)newFixedThreadPool (线程池中的线程数目是固定的,不管来了多少的任务)
package 线程池;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
* @author Heian
* @time 19/03/17 21:11
* @copyright(C) 2019 深圳市北辰德科技股份有限公司
* 用途:
*/
public class ExecutorSer {
public static void sleepOneSecond(){
try {
TimeUnit.SECONDS.sleep (1);
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
public static void sleepFiveSecond(){
try {
TimeUnit.SECONDS.sleep (5);
} catch (InterruptedException e) {
e.printStackTrace ();
}
}
public static void main(String[] args) {
//(1)newFixedThreadPool(线程池中的线程数目是固定的,不管来了多少的任务)
ExecutorService executorService = Executors.newFixedThreadPool (5);
System.out.println ("FixedThreadPool" + executorService);
//[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
for (int i=0;i<6;i++) //定义6个任务
executorService.execute (() -> {
ExecutorSer.sleepOneSecond ();
System.out.println (Thread.currentThread ().getName ());
});
System.out.println ("线程正在执行任务的状态:" +executorService);
//[Running, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0] 如果是三个任务,则pool size=3 说明线程池是懒加载方式创建线程
executorService.shutdown ();//此方法会让所有线程执行完毕后在关闭,shutdownNow:立马关闭
System.out.println ("是否执行shutdown方法:"+ executorService.isShutdown () + "执行shutdown方法后状态:"+ executorService) ;
//true ,[Shutting down, pool size = 5, active threads = 5, queued tasks = 1, completed tasks = 0]
System.out.println ("是否所有线程已经执行完毕:" + executorService.isTerminated ());
ExecutorSer.sleepFiveSecond ();
System.out.println ("5s后,是否执行shutdown方法:"+ executorService.isShutdown () + "执行shutdown方法后状态:"+ executorService) ;
//true 执行shutdown方法后状态:java.util.concurrent.ThreadPoolExecutor@28864e92[Terminated, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 6]
System.out.println ("5s后,是否所有线程已经执行完毕:" + executorService.isTerminated ());//true
}
}
(2)newSingleThreadExecutor(从头到尾整个线程池都只有一个线程在工作)
ExecutorService executorService1 = Executors.newSingleThreadExecutor ();
for (int i=0;i<5;i++)
executorService1.execute (() -> {
System.out.println (Thread.currentThread ().getName ());
});
//可以看到至始至终就只有一个线程在执行任务
/* pool-2-thread-1
pool-2-thread-1
pool-2-thread-1
pool-2-thread-1
pool-2-thread-1*/
(3)newCachedThreadPool
ExecutorService executorService2 = Executors.newCachedThreadPool ();
System.out.println (executorService2);
for (int i=0;i<12;i++)
executorService2.execute (() -> {
ExecutorSer.sleepOneSecond ();
System.out.println (executorService2);
});
System.out.println ("CacheThreadPool线程提交后,线程状态为" + executorService2);
/* java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 0, active threads = 0, queued tasks = 0, completed tasks = 0]
CacheThreadPool线程提交后,线程状态为java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 12, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 12, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 12, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 12, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 12, queued tasks = 0, completed tasks = 0]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 8, queued tasks = 0, completed tasks = 4]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 7, queued tasks = 0, completed tasks = 5]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 6, queued tasks = 0, completed tasks = 6]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 5, queued tasks = 0, completed tasks = 7]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 5, queued tasks = 0, completed tasks = 7]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 3, queued tasks = 0, completed tasks = 9]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 2, queued tasks = 0, completed tasks = 10]
java.util.concurrent.ThreadPoolExecutor@2096442d[Running, pool size = 12, active threads = 1, queued tasks = 0, completed tasks = 11]*/
(4)newScheduledThreadPool (可以在指定延迟后或周期性地执行线程任务的线程池)
//建立四个线程 每个500毫秒去打印当前线程的名称
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool (4);
scheduledExecutorService.scheduleAtFixedRate (() -> {
System.out.println (Thread.currentThread ().getName ());
},0,500,TimeUnit.MILLISECONDS);
callable接口的使用
Callable接口实际上是属于Executor框架中的功能类,Callable接口与Runnable接口的功能类似,但提供了比Runnable更加强大的功能。
- Callable可以在任务结束的时候提供一个返回值,Runnable无法提供这个功能
- Callable的call方法分可以抛出异常,而Runnable的run方法不能抛出异常。
package 线程池.ICallabl;
import java.util.concurrent.*;
/**
* @author Heian
* @time 19/03/23 22:27
* 用途:模拟异步发送接口
*/
public class Icallable {
//Callable泛型为其异步返回的结果类型 Callable函数式接口
static class sendEmail implements Callable<String> {
private long sleepTime;
public sendEmail(long sleepTime) {
this.sleepTime = sleepTime;
}
@Override
public String call() throws Exception {
Thread.sleep (sleepTime);
System.out.println ("发送emails完毕:"+ Thread.currentThread ().getName ());
return Thread.currentThread ().getName ();
}
}
static class sendPhoneMsg implements Callable<String> {
private long sleepTime;
public sendPhoneMsg(long sleepTime) {
this.sleepTime = sleepTime;
}
@Override
public String call() throws Exception {
Thread.sleep (sleepTime);
System.out.println ("发送短信完毕:"+ Thread.currentThread ().getName ());
return Thread.currentThread ().getName ();
}
}
public static void main(String[] args) throws Exception{
long start = System.currentTimeMillis ();
sendEmail sendEmail = new sendEmail (5000);
sendPhoneMsg sendPhoneMsg = new sendPhoneMsg (8000);
FutureTask<String> sendEmailTask = new FutureTask<> (sendEmail);
FutureTask<String> sendPhonneMsgTask = new FutureTask<> (sendPhoneMsg);
ExecutorService executorService = Executors.newFixedThreadPool (3);//线程池中的线程数目是固定的,来了多少任务,启动多少线程
executorService.execute (sendEmailTask);
executorService.execute (sendPhonneMsgTask);
while(true){
if(!sendEmailTask.isDone ()){
TimeUnit.MILLISECONDS.sleep (500);
System.out.println ("enlias发送中,请稍后" + Thread.currentThread ().getName ());
}
if(!sendPhonneMsgTask.isDone ()){
TimeUnit.MILLISECONDS.sleep (500);
System.out.println ("短信发送中,请稍后"+ Thread.currentThread ().getName ());
}
if (sendEmailTask.isDone () && sendPhonneMsgTask.isDone ()){
System.out.println ("两个线程执行完毕");
executorService.shutdown ();//所有线程执行完毕后再关闭
return;
}
//执行完成
long end = System.currentTimeMillis ();
System.out.println ("耗时" +(end-start) + "毫秒");
}
/* while (true){// email:2 短信:5
if (sendEmailTask.isDone ()){//isDone方法表示任务是否已经完成,若任务完成,则返回true;
System.out.println ("email执行完毕" + sendEmailTask.get ());//方法用来获取执行结果,这个方法会产生阻塞,会一直等到任务执行完毕才返回;
}
if (sendEmailTask.isDone () && sendPhonneMsgTask.isDone ()){
System.out.println ("两个线程执行完毕");
executorService.shutdown ();//所有线程执行完毕后再关闭
return;
}
}*/
}
}