当前位置: 首页 > news >正文

Java EE(8)——线程安全总结(翻新版)——定时器(Timer)线程池(ThreadPoolExecutor)

1.Timer

1.1Timer基本介绍

1.Timer的主要作用

任务调度:Timer允许你安排一个任务在未来的某个时间点执行,或者以固定的间隔重复执行
后台执行:Timer可以使用一个后台线程来执行任务,这意味着调度和执行任务不会阻塞主线程(主线程结束后后台线程跟着结束)
简单易用:Timer提供了一个相对简单的方式来处理定时任务,适合用于不需要复杂调度的场景

2.Timer的构造方法

//1.默认构造方法
//创建一个Timer对象,是一个后台线程,并使用线程的默认名字
public Timer() {this("Timer-" + serialNumber());
}
//2.指定线程名字的构造方法
//创建一个Timer对象,是一个后台线程,并使用指定的线程名字
public Timer(String name) {thread.setName(name);thread.start();
}
//3.指定是否为后台线程的构造方法
//传入true,是后台线程;传入false,是前台线程
public Timer(boolean isDaemon) {this("Timer-" + serialNumber(), isDaemon);
}
//4.指定线程名字和是否为后台线程的构造方法
public Timer(String name, boolean isDaemon) {thread.setName(name);thread.setDaemon(isDaemon);thread.start();
}

3.Timer中的schedule方法

(1)schedule(TimerTask task, Date time):安排任务在指定的时间执行一次

public static void main(String[] args) {Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("延迟三秒执行");}};//使用Date对象来指定具体的执行时间//new Date(System.currentTimeMillis()+1000表示当前时间等待1000mstimer.schedule(timerTask,new Date(System.currentTimeMillis()+1000));
}

(2)schedule(TimerTask task, Date firstTime, long period):安排任务在指定的时间首次执行,然后每隔一段时间重复执行

public static void main(String[] args) {Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("延迟三秒执行");}};//当前时间等待1000ms后第一次执行任务//此后每间隔1000ms就执行一次任务timer.schedule(timerTask,new Date(System.currentTimeMillis()+1000),1000);
}

(3)schedule(TimerTask task, long delay):安排任务在指定的延迟时间后执行一次(相对于当前时间)

public static void main(String[] args) {Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("延迟三秒执行");}};//当前时间延迟3000ms后执行timer.schedule(timerTask,3000);
}

(4)schedule(TimerTask task, long delay, long period):安排任务在指定的延迟时间后首次执行,然后每隔一段时间重复执行

public static void main(String[] args) {Timer timer = new Timer();TimerTask timerTask = new TimerTask() {@Overridepublic void run() {System.out.println("延迟三秒执行");}};//当前时间延迟3000ms后执行//此后每间隔3000ms就执行一次任务timer.schedule(timerTask,30003000);
}

问题:上述构造方法中,使用Date对象指定时间和使用long类型数据指定时间有什么区别?

Date对象可以指定一个具体的时间,比如在202511120101秒执行任务;
而传入一个long类型数据表示以当前时间为基准,延迟指定时间后执行任务

1.2模拟实现MyTimer

1.2.1大体框架

MyTimer类:
(1)Thread线程:用来执行任务
(2)PriorityQueue(小根堆):存放任务,并且可以按照时间先后顺序取出任务
(3)MyTimer构造方法:启动thread
(4)schedule:实例化/添加任务
任务
MyTask类
(1)time:保存任务要执行的时间
(2)Runnable:重写run方法
(3)run方法:执行run方法
(4)getTime:获取人物的执行时间
(5)compareTo方法:保证放进小根堆的任务必须是可比较的

1.2.2MyTask类

在这里插入图片描述

1.2.3MyTime类

在这里插入图片描述

1.2.4代码演示

在这里插入图片描述

1.3MyTimer源码

import java.util.PriorityQueue;
/*** Created with IntelliJ IDEA.* Description:* User: 38917* Date: 2025-03-05* Time: 19:26*/
//描述一个任务
class MyTimerTask implements Comparable<MyTimerTask>{private final long time;private final Runnable runnable;public MyTimerTask(Runnable runnable,long delay){this.runnable = runnable;this.time = (System.currentTimeMillis() + delay);}//执行run中的代码块public void run(){runnable.run();}//比较@Overridepublic int compareTo(MyTimerTask o) {return (int)(this.time - o.time);}//获取时间public long getTime() {return time;}
}class MyTimer {//创建线程Thread thread;//存放任务private final PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//创建锁对象private final Object locker = new Object();//实例化任务public void schedule(Runnable runnable,long delay){synchronized (locker) {//实例化任务MyTimerTask myTimerTask = new MyTimerTask(runnable, delay);//添加任务queue.offer(myTimerTask);locker.notify();}}//扫描任务队列,执行任务的线程public MyTimer(){thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {//锁是保护队列try {synchronized (locker) {while (queue.isEmpty()) {locker.wait();}MyTimerTask myTimerTask = queue.peek();long curTime = System.currentTimeMillis();if (curTime >= myTimerTask.getTime()) {queue.poll();//执行任务myTimerTask.run();} else {locker.wait(myTimerTask.getTime() - curTime);}}} catch (InterruptedException e) {throw new RuntimeException(e);}}});thread.setDaemon(true);thread.start();}
}public class Main {public static void main(String[] args) throws InterruptedException {MyTimer myTimer = new MyTimer();myTimer.schedule(() -> System.out.println("400"),400);myTimer.schedule(() -> System.out.println("300"),300);myTimer.schedule(() -> System.out.println("200"),200);myTimer.schedule(() -> System.out.println("100"),100);Thread.sleep(500);System.out.println("Hello Main");}
}

2.线程池

2.1什么是线程池?

1.概念:线程池是一种管理和复用线程的编程模式。它预先创建一定数量的线程,在执行任务需要时,将任务分配给这些线程,从而提高运行效率

2.主要特点
线程复用:当线程执行完一个任务时,不会立即销毁,而是等待下一个任务的到来(当然这种等待是有时间限制的),这样避免了频繁的创建和销毁线程
动态调整:根据实际环境需要动态调整线程数量,以达到最佳性能
任务队列:线程池会维护一个任务队列,用于存放待执行的任务,当线程空闲时,从队列中取出任务并执行

2.2 标准库线程池参数介绍

在这里插入图片描述
1.int corePoolSize:核心线程数
2.int maximumPoolSize:最大线程数
3.long keepAliveTime:非核心线程的空闲时的最大存活时间。假如corePoolSize=4,maximumPoolSize=8,有四个非核心线程,这类线程空闲时,不能一直等待新任务的到来,当等待时间超过keepAliveTime后就会销毁
4.TimeUnit unit:long keepAliveTime的时间单位
5.BlockingQueue workQueue:任务队列
6.ThreadFactory threadFactory:线程工厂,用于创建新线程的工厂
7.RejectedExecutionHandler handler拒绝策略

2.3线程池的执行流程

假设现在有一个线程池:
核心线程数2,最大线程数4,等待队列2
(1)任务数量<=2(A,B)时,由核心线程执行任务
(2) 2<任务数量<=4(A,B,C,D)时,核心线程无法同时处理所有任务,未被执行的任务(C,D)将会进入等待队列中等待核心线程执行
(3)4<任务数量<=6(A,B,C,D,E,F),此时等待队列也满了,线程池就会就会开放非核心线程来执行任务,C和D任务继续在等待队列中等待,新添加的E和F任务由非核心线程来执行
(4)任务数量>6,核心线程,等待队列,非核心线程都被任务所占用,仍然无法满足需求,此时就会触发线程池的拒绝策略

RejectedExecutionHandler handler四大拒绝策略

在这里插入图片描述
1.AbortPolicy:直接抛异常
在这里插入图片描述
2.CallerRunsPolicy:由提交该任务的线程来执行在这里插入图片描述
3.DiscardPolicy:丢弃新任务
在这里插入图片描述
4.DiscardOldestPolicy:丢弃最老的任务
在这里插入图片描述

2.4模拟实现一个简单的线程池

在这里插入图片描述

3.线程安全问题总结

(1)使用没有共享资源的模型

(2)使用共享资源只读,不写的模型

  1.  不需要写共享资源的模型
    
  2.  使用不可变对象
    

(3)直面线程安全(重点)

  1.  保证原子性:synchronized
    
  2.  保证顺序性:volatile(取消指令重排序)
    
  3.  保证可见性:synchronized(上锁时将主内存中的数据同步到工作内存,解锁时将工作内存的数据同步到主内存),volatile(强制从主内存读取数据)
    

4.小结

基础的线程安全问题以及常见的四种设计模式(单例生产者&消费者定时器线程池)至此全部都介绍完毕了,下节开始就从锁策略开始讲解

相关文章:

  • LeetCode算法题(Go语言实现)_48
  • 智能做题:拍照解题API使用指南
  • AI对百度搜索与抖音社区的影响差异?
  • 【Python语言基础】20、模块与包
  • 用 Vue 3 + OpenAI API 实现一个智能对话助手(支持上下文、多角色)
  • fastjson2 使用bug
  • 每日算法-链表(2.两数相加、24.两两交换链表中的节点、143.重排链表)
  • 怎么通过OPPO手机进行图片编辑?图片编辑攻略,打造专业级照片
  • vscode格式化为什么失效?自动保存和格式化(Prettier - Code formatter,vue-format)
  • MySQL入门:数据表的创建
  • HTTP HTTPS RSA
  • AI核心概念之“结构化输出(Structured Output)” - 来自DeepSeek
  • Terraform - 理解 Count 和 For_Each 循环
  • 力扣 283 移动零的两种高效解法详解
  • 深度学习--神经网络的构造
  • AI核心概念之“提示(Prompt)” - 来自DeepSeek
  • Python字符串操作全解析:从基础到高阶应用
  • mysql关联查询语句
  • C/C++条件判断
  • 混合开发部署实战:PyInstaller + .NET 8 + Docker全链路配置
  • 张译、惠英红分获第二十届中国电影华表奖优秀男、女演员奖
  • 我国对国家发展规划专门立法
  • 酒店保洁员调包住客港币,海南官方通报:成立调查组赴属地调查
  • 文化体验+商业消费+服务创新,上海搭建入境旅游新模式
  • “爱泼斯坦案”关键证人弗吉尼亚·朱弗雷自杀身亡
  • 白俄罗斯驻华大使:应发挥政党作用,以对话平台促上合组织发展与合作