目录
Executors创建线程池默认方法
newFixedThreadPool()方法,该方法返回一个固定数量的线程池,该方法的线程数始终不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中等待有空闲的线程去执行。
newSingleThreadExecutor()方法,创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列。
newCachedThreadPool()方法,返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若有空闲的线程则直接执行任务,若无空闲则创建线程,若无任务则不创建线程。并且每一个空闲线程会在60秒后自动回收。
newScheduledThreadPool()该方法返回一个ScheduledExecutorService对象,但该线程池可以指定线程的数量,可以定时。
前三种线程池添加线程:
ExecutorService pool = Executors.newSingleThreadExecutor();
pool.execute(new Thread());
//submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值
Future f1 = pool.submit(new Thread());
f1.get()//如果为空,则线程执行完毕了,否则会一直阻塞在这里。
//ScheduledThreadPool相当于定时任务
class Temp extends Thread {
public void run() {
System.out.println("run");
}
}
public class ScheduledJob {
public static void main(String args[]) throws Exception {
Temp command = new Temp();
ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
// 该方法,会在目标方法执行完之后,间隔1秒执行
ScheduledFuture<?> scheduleTask = scheduler.scheduleWithFixedDelay(command, 5, 1, TimeUnit.SECONDS);//5秒初始化之后执行一次,以后每1秒执行一次
// scheduleAtFixedRate ,会按照固定的频率执行,跟方法执行时间没关系
}
}
其实底层都是new了ThreadPoolExecutor。
若Executors工厂类无法满足我们的需求,可以自己去创建自定义的线程池,其实Executors工厂类里面的创建线程方法其内部实现均是用了ThreadPoolExecutor这个类,这个类可以自定义线程。构造方法如下:
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {}
* corePoolSize: 线程池核心线程数(线程池初始化存在的线程)
* maximumPoolSize:线程池最大数
* keepAliveTime: 空闲线程存活时间
* unit: 时间单位
* workQueue: 线程池所使用的缓冲队列
* threadFactory:线程池创建线程使用的工厂
* handler: 线程池对拒绝任务的处理策略
自定义线程池
这个构造方法对于队列是什么类型的比较关键。
使用有界队列时:若有新的任务需要执行,如果线程池实际线程小于corePoolSize,则优先创建线程,若大于corePoolSize,则会将任务加入队列,若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程去执行新任务,若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
使用无界队列时:LinkedBlockingQueue。与有界队列相比,除非系统资源耗尽,否则无界的任务队列不存在任务入队失败的情况。当有新任务到来,系统的线程数小于corePoolSize时,则新建线程执行任务。当达到corePoolSize后,就不会继续增加。若后续仍有新的任务加入,而又没有空闲的线程资源,则任务直接进入队列等待。若任务创建和处理的速度差异很大,无界队列会保持快速增长,直到耗尽系统内存。
JDK拒绝策略:
AbortPolicy:直接抛出异常组织系统正常工作。
CallerRunsPolicy:只要线程池未关闭,该策略直接在调用者线程中,运行当前被丢弃的任务。
DiscardOldestPolicy:丢弃最老的一个请求,尝试再次提交当前任务。
DiscardPolicy:丢弃无法处理的任务,不给予任何处理。
//如果需要自定义拒绝策略可以实现RejectedExecutionHandler接口。
public class MyTask implements Runnable {
private int taskId;
private String taskName;
public MyTask(int taskId, String taskName){
this.taskId = taskId;
this.taskName = taskName;
}
public int getTaskId() {
return taskId;
}
public void setTaskId(int taskId) {
this.taskId = taskId;
}
public String getTaskName() {
return taskName;
}
public void setTaskName(String taskName) {
this.taskName = taskName;
}
@Override
public void run() {
try {
System.out.println("run taskId =" + this.taskId);
Thread.sleep(5*1000);
//System.out.println("end taskId =" + this.taskId);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String toString(){
return Integer.toString(this.taskId);
}
}
public class UseThreadPoolExecutor1 {
public static void main(String[] args) {
/**
* 在使用有界队列时,若有新的任务需要执行,如果线程池实际线程数小于corePoolSize,则优先创建线程,
* 若大于corePoolSize,则会将任务加入队列,
* 若队列已满,则在总线程数不大于maximumPoolSize的前提下,创建新的线程,
* 若线程数大于maximumPoolSize,则执行拒绝策略。或其他自定义方式。
*
*/
ThreadPoolExecutor pool = new ThreadPoolExecutor(
1, //coreSize
2, //MaxSize
60, //60
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3) //指定一种队列 (有界队列)
//new LinkedBlockingQueue<Runnable>()
, new MyRejected()
//, new DiscardOldestPolicy()
);
MyTask mt1 = new MyTask(1, "任务1");
MyTask mt2 = new MyTask(2, "任务2");
MyTask mt3 = new MyTask(3, "任务3");
MyTask mt4 = new MyTask(4, "任务4");
MyTask mt5 = new MyTask(5, "任务5");
MyTask mt6 = new MyTask(6, "任务6");
pool.execute(mt1);
pool.execute(mt2);
pool.execute(mt3);
pool.execute(mt4);
pool.execute(mt5);
pool.execute(mt6);
pool.shutdown();
}
}
public class MyRejected implements RejectedExecutionHandler{
public MyRejected(){
}
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("自定义处理..");
System.out.println("当前被拒绝任务为:" + r.toString());
}
}
public class UseThreadPoolExecutor2 implements Runnable{
private static AtomicInteger count = new AtomicInteger(0);
@Override
public void run() {
try {
int temp = count.incrementAndGet();
System.out.println("任务" + temp);
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception{
//System.out.println(Runtime.getRuntime().availableProcessors());
BlockingQueue<Runnable> queue =
//new LinkedBlockingQueue<Runnable>();
new ArrayBlockingQueue<Runnable>(10);
ExecutorService executor = new ThreadPoolExecutor(
5, //core
10, //max无界队列的这个参数其实没啥作用了
120L, //2fenzhong
TimeUnit.SECONDS,
queue);
for(int i = 0 ; i < 20; i++){
executor.execute(new UseThreadPoolExecutor2());
}
Thread.sleep(1000);
System.out.println("queue size:" + queue.size()); //10
Thread.sleep(2000);
}
}
线程池的方法
向线程池提交任务
execute()
execute()方法用于提交不需要返回值的任务Runnable,所以无法判断任务是否被线程池执行成功。
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
import java.util.stream.IntStream;
@Slf4j
public class ExecuteDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
IntStream.rangeClosed(1, 3).forEach(i -> threadPoolExecutor.execute(() -> log.info("Task {} working...", i))); // 提交任务
threadPoolExecutor.shutdown(); // 关闭线程池
}
}
运行结果如下:
2020-10-13 14:58:09,500 INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 1 working...
2020-10-13 14:58:09,502 INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 2 working...
2020-10-13 14:58:09,502 INFO [pool-1-thread-1] (ExecuteDemo.java:12) - Task 3 working...
submit()
submit()方法用于提交需要返回值的任务。线程池会返回一个future类型的对象,通过这个future对象可以判断任务是否执行成功,并且可以通过future的get()方法来获取返回值,get()方法会阻塞当前线程直到任务完成,get()方法也支持带超时时间的阻塞。
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.*;
import java.util.stream.IntStream;
@Slf4j
public class SubmitDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
Future<String> future = threadPoolExecutor.submit(() -> {
log.info("Task begin...");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("Task end.");
return "task result";
});
threadPoolExecutor.shutdown(); // 关闭线程池
String result = future.get(); // 阻塞等待获取结果
log.info("get result:{}", result);
}
}
运行结果如下:
2020-10-13 15:03:22,306 INFO [pool-1-thread-1] (SubmitDemo.java:14) - Task begin...
2020-10-13 15:03:25,315 INFO [pool-1-thread-1] (SubmitDemo.java:20) - Task end.
2020-10-13 15:03:25,316 INFO [main] (SubmitDemo.java:27) - get result:task result
invokeAll()
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Slf4j
public class InvokeAllDemo {
public static void main(String[] args) throws InterruptedException, ExecutionException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
List<Callable<String>> tasks = IntStream.rangeClosed(1, 3).boxed().map(i -> (Callable<String>) () -> "task " + i).collect(Collectors.toList());
List<Future<String>> futures = threadPoolExecutor.invokeAll(tasks);// 批量提交任务
for (Future<String> future : futures) {
log.info("result:{}", future.get()); // 阻塞获取结果
}
threadPoolExecutor.shutdown();
}
}
运行结果如下:
2020-10-13 16:09:00,518 INFO [main] (InvokeAllDemo.java:25) - result:task 1
2020-10-13 16:09:00,520 INFO [main] (InvokeAllDemo.java:25) - result:task 2
2020-10-13 16:09:00,520 INFO [main] (InvokeAllDemo.java:25) - result:task 3
invokeAny()
invokeAny批量提交任务,哪个任务先执行完毕,立刻返回此任务执行结果,其它任务取消。
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@Slf4j
public class InvokeAnyDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(3, 3, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
List<Callable<String>> tasks = IntStream.rangeClosed(1, 3).boxed().map(i -> (Callable<String>) () -> {
log.info("task{} begin ...", i);
TimeUnit.SECONDS.sleep(new Random().nextInt(10));
log.info("task{} end .", i);
return "task" + i;
}).collect(Collectors.toList());
String result = threadPoolExecutor.invokeAny(tasks); // 哪个任务执行完就立马返回,其他任务直接中断
log.info("result:{}", result); // 阻塞获取结果
threadPoolExecutor.shutdown();
}
}
运行结果如下:
2020-10-13 16:09:35,121 INFO [pool-1-thread-1] (InvokeAnyDemo.java:21) - task1 begin ...
2020-10-13 16:09:35,121 INFO [pool-1-thread-2] (InvokeAnyDemo.java:21) - task2 begin ...
2020-10-13 16:09:35,121 INFO [pool-1-thread-3] (InvokeAnyDemo.java:21) - task3 begin ...
2020-10-13 16:09:38,125 INFO [pool-1-thread-3] (InvokeAnyDemo.java:23) - task3 end .
2020-10-13 16:09:38,125 INFO [main] (InvokeAnyDemo.java:29) - result:task3
线程池的关闭
shutdown()
shutdown()方法会先将线程池的状态改为SHUTDOWN,然后中断空闲的线程,不会再接收新任务,但已提交任务会执行完。
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;
/**
* 线程池的关闭之shutdown()
*/
@Slf4j
public class ShutdownDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
IntStream.rangeClosed(1, 10).forEach(i -> threadPoolExecutor.submit(() -> {
log.info("task{} begin ...", i);
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(3));
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("task{} end .", i);
}));
threadPoolExecutor.shutdown();
threadPoolExecutor.submit(() -> log.info("submit task after shutdown"));
}
}
运行结果如下:
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@18769467 rejected from java.util.concurrent.ThreadPoolExecutor@46ee7fe8[Shutting down, pool size = 2, active threads = 2, queued tasks = 8, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:112)
at com.morris.concurrent.threadpool.threadpoolexecutor.api.ShutdownDemo.main(ShutdownDemo.java:30)
2020-10-13 16:54:42,637 INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task1 begin ...
2020-10-13 16:54:42,637 INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task2 begin ...
2020-10-13 16:54:43,639 INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task2 end .
2020-10-13 16:54:43,639 INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task1 end .
2020-10-13 16:54:43,640 INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task3 begin ...
2020-10-13 16:54:43,640 INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task4 begin ...
2020-10-13 16:54:43,641 INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task4 end .
2020-10-13 16:54:43,641 INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task5 begin ...
2020-10-13 16:54:44,642 INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task5 end .
2020-10-13 16:54:44,642 INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task6 begin ...
2020-10-13 16:54:45,642 INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task3 end .
2020-10-13 16:54:45,642 INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task7 begin ...
2020-10-13 16:54:46,642 INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task7 end .
2020-10-13 16:54:46,642 INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task6 end .
2020-10-13 16:54:46,642 INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task8 begin ...
2020-10-13 16:54:46,642 INFO [pool-1-thread-1] (ShutdownDemo.java:19) - task9 begin ...
2020-10-13 16:54:47,642 INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task8 end .
2020-10-13 16:54:47,642 INFO [pool-1-thread-2] (ShutdownDemo.java:19) - task10 begin ...
2020-10-13 16:54:47,642 INFO [pool-1-thread-2] (ShutdownDemo.java:25) - task10 end .
2020-10-13 16:54:48,643 INFO [pool-1-thread-1] (ShutdownDemo.java:25) - task9 end .
shutdownNow()
shutdownNow()会先将会先将线程池的状态改为STOP,不会再接收新任务,然后中断所有的线程,如果执行中的任务没有对中断进行处理,那么这个任务将会继续执行直至完成,如果执行中的任务对中断进行的处理,那么将按中断进行执行,最后方法会返回等待队列中未执行的任务。
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.List;
import java.util.Random;
import java.util.concurrent.*;
import java.util.stream.IntStream;
/**
* 线程池的关闭之shutdownNow()
*/
@Slf4j
public class ShutdownNowDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2, 2, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
IntStream.rangeClosed(1, 10).forEach(i -> threadPoolExecutor.submit(() -> {
log.info("task{} begin ...", i);
try {
TimeUnit.SECONDS.sleep(new Random().nextInt(3));
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("task{} end .", i);
}));
List<Runnable> runnables = threadPoolExecutor.shutdownNow();
log.info("tasks size:{}", runnables.size());
}
}
运行结果如下:
2020-10-13 16:55:08,985 INFO [main] (ShutdownNowDemo.java:30) - tasks size:8
2020-10-13 16:55:08,987 INFO [pool-1-thread-2] (ShutdownNowDemo.java:20) - task2 begin ...
2020-10-13 16:55:08,987 INFO [pool-1-thread-1] (ShutdownNowDemo.java:20) - task1 begin ...
2020-10-13 16:55:08,988 INFO [pool-1-thread-2] (ShutdownNowDemo.java:26) - task2 end .
2020-10-13 16:55:08,989 INFO [pool-1-thread-1] (ShutdownNowDemo.java:26) - task1 end .
awaitTermination()
调用shutdown()后,调用线程并不会等待所有任务运行结束,可以利用awaitTermination()方法设置一个超时时间进行阻塞等待,此方法返回了有两种情况,要么所有的任务执行完成,要么超时时间到了。
package com.morris.concurrent.threadpool.threadpoolexecutor.api;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* threadPoolExecutor.awaitTermination()的使用
*/
@Slf4j
public class AwaitTerminationDemo {
public static void main(String[] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS, new LinkedBlockingQueue<>());
threadPoolExecutor.submit(() -> {
log.info("task begin ...");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("task end .");
});
threadPoolExecutor.shutdown(); // 关闭线程池
threadPoolExecutor.awaitTermination(10, TimeUnit.SECONDS);
log.info("main thread exit");
}
}
运行结果如下:
2020-10-13 17:01:45,397 INFO [pool-1-thread-1] (AwaitTerminationDemo.java:16) - task begin ...
2020-10-13 17:01:48,398 INFO [pool-1-thread-1] (AwaitTerminationDemo.java:22) - task end .
2020-10-13 17:01:48,398 INFO [main] (AwaitTerminationDemo.java:27) - main thread exit
创建线程的工厂ThreadFactory
可以自定义ThreadFactory为线程池中的线程取一个有意义的名字,方便后面快速定位问题。
package com.morris.concurrent.threadpool.threadpoolexecutor.construct;
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 使用ThreadFactory自定义线程名
*/
@Slf4j
public class ThreadFactoryDemo {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(1, 1, 0, TimeUnit.SECONDS,
new LinkedBlockingQueue<>(), new ThreadFactory() {
private AtomicInteger idx = new AtomicInteger(1);
private static final String THREAD_NAME_PREFIX = "mythread-pool-";
@Override
public Thread newThread(Runnable r) {
return new Thread(r, THREAD_NAME_PREFIX + idx.getAndIncrement());
}
});
threadPoolExecutor.submit(()->{
log.info("task begin ...");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("task end .");
});
threadPoolExecutor.shutdown();
}
}