线程池是一种用于管理和复用线程的机制。它由一个线程队列和一组管理线程的方法组成。线程池中的线程可以被重复使用,用于执行提交的任务,而不需要每次都创建和销毁线程。
线程队列:线程池维护一个线程队列,用于存储待执行的任务。当有任务提交到线程池时,线程池会从队列中取出一个空闲的线程来执行任务。
线程管理器:线程池的线程管理器负责创建、启动和停止线程。它会根据需要动态地创建线程,并将线程添加到线程队列中。当线程池不再需要某个线程时,线程管理器会停止该线程并从线程队列中移除。
任务接口:线程池中的任务接口定义了待执行的任务的类型。任务可以是实现了Runnable接口的普通任务,也可以是实现了Callable接口的有返回值的任务。
任务队列:线程池的任务队列用于存储待执行的任务。当有任务提交到线程池时,线程池会将任务加入到任务队列中,等待线程来执行。
提交任务的两种方式
具体业务类
public class TaskToRunnable implements Runnable{
@Override
public void run() {
//打印线程的名字
System.out.println("线程名:"+Thread.currentThread().getName());
}
}
客户端
public class Client {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建任务无返回值
TaskToRunnable taskToRunnable=new TaskToRunnable();
//创建单个线程的线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//提交任务
threadPool.execute(taskToRunnable);
//关闭线程池
threadPool.shutdown();
}
}
具体业务类
public class TaskToCallable implements Callable<Integer> {
@Override
public Integer call() {
return 5+5;
}
}
客户端
public class Client {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建任务有返回值
TaskToCallable taskToCallable=new TaskToCallable();
//创建单个线程的线程池
ExecutorService threadPool = Executors.newSingleThreadExecutor();
//提交任务
Future<Integer> submit = threadPool.submit(taskToCallable);
try{
System.out.println("返回值为:"+submit.get());
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程池
threadPool.shutdown();
}
}
}
取消任务分为三个阶段包括:取消未执行的任务、取消正在运行的任务和取消已完成的任务下面通过代码的方式给大家展示他们的效果
客户端代码
/**
* @BelongsProject: demo
* @BelongsPackage: com.example.threadpool.cancel
* @Author: Wuzilong
* @Description: 取消未执行的任务
* @CreateTime: 2023-07-23 23:42
* @Version: 1.0
*/
public class UnExecutedClient {
public static void main(String[] args) {
TaskToCallable taskToCallable=new TaskToCallable();
ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
Future<Integer> future1 = threadExecutor.submit(taskToCallable);
Future<Integer> future2 = threadExecutor.submit(taskToCallable);
boolean cancel = future2.cancel(false);
System.out.println("任务2是否取消成功"+cancel);
try{
Integer integer = future2.get();
System.out.println("任务2的返回值"+integer);
}catch (Exception e){
e.printStackTrace();
}finally {
threadExecutor.shutdown();
}
}
}
业务代码三个实例都是一样的下面的两个示例就不在写了
/**
* @BelongsProject: demo
* @BelongsPackage: com.example.threadpool.commit
* @Author: Wuzilong
* @Description: 业务代码
* @CreateTime: 2023-07-23 22:58
* @Version: 1.0
*/
public class ExecuteClientTask implements Callable<Integer> {
@Override
public Integer call() throws Exception {
int i=0;
//线程没有被中断递增i
while(!Thread.interrupted()){
i++;
}
System.out.println("当前i的值为:"+i);
return 5+5;
}
}
我们创建了一个单个线程的线程池,并创建了两个线程。这样一个线程在线程队列中一个线程在任务队列中,我们取消在任务队列中的线程,获取获取一下取消了的线程的值。会发现获取值的时候会报错,提示线程已取消。
客户端代码
/**
* @BelongsProject: demo
* @BelongsPackage: com.example.threadpool.cancel
* @Author: Wuzilong
* @Description: 取消执行中的任务
* @CreateTime: 2023-07-23 23:50
* @Version: 1.0
*/
public class ExecuteClient {
public static void main(String[] args) {
ExecuteClientTask taskToCallable=new ExecuteClientTask();
ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
Future<Integer> future = threadExecutor.submit(taskToCallable);
System.out.println("任务是否完成:"+future.isDone());
//参数为false执行结果显示取消成功但是程序没有结束 任务继续执行完
//参数为true直接结果显示取消成功程序结束了
boolean cancel = future.cancel(true);
System.out.println("任务1是否取消成功"+cancel);
try{
Integer integer = future.get();
System.out.println("获取任务1的结果:"+integer);
}catch (Exception e){
e.printStackTrace();
}finally {
threadExecutor.shutdown();
}
}
}
运行结果
我们同样创建了一个当个线程的线程池,提交完任务之后直接执行取消操作,我们在执行取消操作的时候会传入一个参数,true或者false:参数为false执行结果显示取消成功但是程序没有结束,任务继续执行完。参数为true直接结果显示取消成功程序结束了。上面示例中传入的是true下面在获取值的时候是获取不到的,直接报线程取消的错。
客户端代码
/**
* @BelongsProject: demo
* @BelongsPackage: com.example.threadpool.cancel
* @Author: Wuzilong
* @Description: 取消已完成的任务 无法取消
* @CreateTime: 2023-07-23 23:54
* @Version: 1.0
*/
public class ExecutedClient {
public static void main(String[] args) {
TaskToCallable taskToCallable=new TaskToCallable();
ExecutorService threadExecutor= Executors.newSingleThreadExecutor();
Future<Integer> future = threadExecutor.submit(taskToCallable);
try{
Integer integer = future.get();
System.out.println("获取任务1的结果:"+integer);
//参数只对运行时的任务有效
boolean cancel = future.cancel(false);
System.out.println("任务1是否取消成功"+cancel);
System.out.println("任务1的返回值"+integer);
}catch (Exception e){
e.printStackTrace();
}finally {
threadExecutor.shutdown();
}
}
}
运行结果
我们同样创建了一个当个线程的线程池,提交完任务之后获取了一下任务的返回结果,然后在去取消任务。发现取消之后还是能够获取到任务的返回值的,所以执行完的任务是无法取消的。
业务逻辑
public class Task implements Runnable{
private final int index;
public Task(int index){
this.index=index;
}
@Override
public void run() {
System.out.println("输出线程名:"+Thread.currentThread().getName()+"线程编号:"+index);
}
}
客户端
/**
* @BelongsProject: demo
* @BelongsPackage: com.example.threadpool.refuse
* @Author: Wuzilong
* @Description: 线程池拒绝任务
* @CreateTime: 2023-07-24 01:08
* @Version: 1.0
*/
public class Client {
public static void main(String[] args) {
//直接丢弃,只有前两个线程
// ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.DiscardPolicy());
//拒绝的任务交个调用者的线程去执行
// ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.CallerRunsPolicy());
//默认的拒绝策略,直接抛出异常信息
// ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
//丢弃队列头部的任务,添加新的任务
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(2),new ThreadPoolExecutor.DiscardOldestPolicy());
try{
threadPoolExecutor.execute(new Task(1));
threadPoolExecutor.execute(new Task(2));
threadPoolExecutor.execute(new Task(3));
//丢弃头部的任务需要打开,前三个注释掉即可
threadPoolExecutor.execute(new Task(4));
}catch (Exception e){
e.printStackTrace();
}finally {
//关闭线程
threadPoolExecutor.shutdown();
}
}
}
运行结果
我们现在使用的是DiscardOldestPolicy拒绝策略创建了一个一个线程队列并且两个任务队列的线程池我们向线程池中添加了四个线程,执行的结果是第二个线程没有执行,执行了第四个线程,这和我们设置的DiscardOldestPolicy拒绝策略是一致的,把任务队列中的头线程给取消了新的线程添加到任务队列中了。
关闭线程池之后,线程池不会继续接收新的任务,并且会继续执行完任务队列中的任务
/**
* @BelongsProject: demo
* @BelongsPackage: com.example.threadpool.close
* @Author: Wuzilong
* @Description: 关闭线程池,不继续接收新的任务,会继续执行完任务队列中的任务
* @CreateTime: 2023-07-24 08:54
* @Version: 1.0
*/
public class Client {
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor=new ThreadPoolExecutor(1,1,0L, TimeUnit.SECONDS,new LinkedBlockingQueue<>(1),new ThreadPoolExecutor.AbortPolicy());
try{
threadPoolExecutor.execute(new Task(1));
//将任务队列中的任务继续执行完毕
threadPoolExecutor.execute(new Task(3));
}catch (Exception e){
e.printStackTrace();
}finally {
threadPoolExecutor.shutdown();
//关闭线程之后继续提交任务看看是否被拒绝
//threadPoolExecutor.execute(new Task(2));
}
}
}
客户端代码
public class Demo {
public static void main(String[] args) {
Task task=new Task();
ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
System.out.println("提交任务时间"+LocalTime.now());
scheduledExecutorService.schedule(task,3, TimeUnit.SECONDS);
scheduledExecutorService.shutdown();
}
}
具体业务代码
public class Task implements Runnable{
@Override
public void run() {
System.out.println("执行方法时间"+ LocalTime.now());
}
}
运行结果
提交Runnable任务和提交Callable任务是一样的。只不过把实现的接口调整一下即可。这里就不过多的演示。schedule方法中的参数:第一个是任务对象,第二个是时间,第三个是时间单位。
具体业务代码
public class Task implements Runnable{
@Override
public void run() {
System.out.println("执行方法时间"+ LocalTime.now());
try{
//线程休眠1秒钟,模拟任务执行时长
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
客户端代码
public class Demo {
public static void main(String[] args) {
Task task=new Task();
ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
System.out.println("提交任务时间"+LocalTime.now());
scheduledExecutorService.scheduleAtFixedRate(task,1,1, TimeUnit.SECONDS);
try{
//当前下次线程休眠5秒
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
scheduledExecutorService.shutdown();
}
}
运行结果
每隔一秒钟执行一次,scheduleAtFixedRate方法中传入的参数为需要执行的任务和延迟时间和每次延迟的固定时间,示例中传入的是1表示每间隔一秒执行一次,最有一个就是时间的单位了。
客户端代码
public class Demo {
public static void main(String[] args) {
Task task=new Task();
ScheduledExecutorService scheduledExecutorService= Executors.newScheduledThreadPool(5);
System.out.println("提交任务时间"+LocalTime.now());
scheduledExecutorService.scheduleWithFixedDelay(task,2,1, TimeUnit.SECONDS);
try{
//当前下次线程休眠5秒
TimeUnit.SECONDS.sleep(5);
}catch (Exception e){
e.printStackTrace();
}
scheduledExecutorService.shutdown();
}
}
具体业务代码和固定时间中的是一致的这里就不重复写了。
运行结果
在前一个任务执行完的前提下每隔两秒钟执行一次,scheduleWithFixedDelay方法中传入的参数为需要执行的任务和延迟时间和每次延迟的固定时间,示例中传入的是2表示在前一个任务执行完的前提下每隔两秒钟执行一次,最有一个就是时间的单位了。