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

javaEE->多线程:定时器

一. 定时器

        约定一个时间,时间到了,执行某个代码逻辑(进行网络通信时常见)

        客户端给服务器发送请求 之后就需要等待 服务器的响应,客户端不可能无限的等,需要一个最大的期限。这里“等待的最大时间”可以用定时器的方式实现。

        标准库中有现成的定时器实现:

         

主线程执行schedule方法的时候,就是把这个任务放到timer对象中。

timer里面本身也含一个线程——>"扫描线程"

时间到了,扫描线程就会执行刚才安排的任务

执行完任务之后,进程并未结束,timer内部的线程阻止了进程的结束。

timer里面也可安排多个任务

public class Demo28 {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {public void run() {System.out.println("执行第四个任务");}},4000);timer.schedule(new TimerTask() {public void run() {System.out.println("执行第三个任务");}},3000);timer.schedule(new TimerTask() {public void run() {System.out.println("执行第二个任务");}},2000);timer.schedule(new TimerTask() {public void run() {System.out.println("执行第一个任务");}},1000);System.out.println("程序启动!");}
}

自己实现一个定时器

1.Timre里面需要有一个线程,扫描任务是否到时间

2.需要有一个数据结构来保存所有的任务

3.需要创建一个类,通过类的对象来描述一个任务。(至少包括任务的内容和时间)

由于任务都带有一个时间的先后顺序,所以我们采用优先级队列的数据结构来实现。

package thread;//自己实现的定时器import java.util.PriorityQueue;//通过一个类来描述任务
class MyTimerTask implements Comparable<MyTimerTask>{//执行的任务private Runnable runnnable;//执行任务的时间private long time;//delay是schedule方法传入的“相对时间”public MyTimerTask(Runnable runnable, long delay) {this.runnnable = runnable;this.time = System.currentTimeMillis()+ delay;}public int compareTo(MyTimerTask o) {//让队首元素是最小时间的值return (int)(this.time-o.time);}public long getTime() {return time;}public Runnable getRunnable() {return runnnable;}
}//定时器的结构
class MyTimer {//通过优先级队列来存储所有的任务,里面的元素务必使可比较的(TreeSet/TreeMap也要求这样)private PriorityQueue<MyTimerTask> queue = new PriorityQueue<>();//锁对象Object locker = new Object();public void schedule(Runnable runnable, long delay) {synchronized (locker) {queue.offer(new MyTimerTask(runnable,delay));//添加新的任务就唤醒扫描线程locker.notify();}}//扫描线程public MyTimer() {Thread t = new Thread(() -> {//不停地扫描队首元素,看是否到达时间while(true) {try{synchronized (locker) {while(queue.isEmpty()) {//用循环的话,是为了确认一下唤醒之后是不是真的不为空了//如果队列为空,就需要等待//等待另外的线程添加新的任务唤醒locker.wait();}MyTimerTask task = queue.peek();//队首元素,最早的的任务long curTime = System.currentTimeMillis();//当前的时间if(curTime >= task.getTime()) {//到了时间,该执行任务了task.getRunnable().run();//执行任务之后,可从队列中删除queue.poll();}else {//还未到时间locker.wait(task.getTime() -curTime);//线程还需要等待的时间//如果不加上wait的话,线程就处于忙等,会消耗很多cpu资源//用上wait阻塞之后呢,线程不会在cpu上调度,把资源让给别人}}}catch (InterruptedException e) {e.printStackTrace();}}});t.start();}}
public class Demo29 {public static void main(String[] args) {MyTimer timer = new MyTimer();timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("3000");}},3000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("2000");}},2000);timer.schedule(new Runnable() {@Overridepublic void run() {System.out.println("1000");}},1000);}
}

二.线程池

线程诞生的意义:因为进程的创建/销毁,太重量了(比较慢)

但如果近一步提高创建/销毁的频率,线程的开销也不容忽视。

有两种方法可以提高效率:
1.协程(轻量级线程):相对于线程,把系统调度的过程省略了。

使用协程更多的是go和python

不知道协程能提升多少,防止出现bug,java一般使用线程

2.线程池:帮线程兜底,不至于很慢

(内存池、线程池、进程池含义类似)

在使用第一个线程的时候,提前把2,3,4,5(其余)线程创建好;

后续如果想要使用新的线程,不必重新创建,直接调用即可,这样的话创建线程的开销就减少了。

调用线程比创建新线程效率跟高

1.调用线程是纯粹“用户态”的操作

2.创建新的线程 是需要“用户态+内核态”共同完成的 

内核态和用户态:

一段程序在系统内核执行 -> 内核态

  

http://www.xdnf.cn/news/10578.html

相关文章:

  • 【Hot 100】45. 跳跃游戏 II
  • Python训练第四十一天
  • 【创新实训个人博客】实现了新的前端界面
  • CP4-OFDM模糊函数原理及仿真
  • 三方接口设计注意事项
  • 人工智能在智能能源管理中的创新应用与未来趋势
  • ContentProvider URI匹配机制详解
  • DELETE 与 TRUNCATE、DROP 的区别
  • 【Java基础】Java基础语法到高级特性
  • Canvas: trying to draw too large(256032000bytes) bitmap.
  • 02.上帝之心算法用GPU计算提速50倍
  • Java对象的内存结构
  • 华为IP(7)
  • 工作流引擎-04-流程引擎(Process Engine)activiti 优秀开源项目
  • 【AI论文】SWE-rebench:一个用于软件工程代理的任务收集和净化评估的自动化管道
  • 搭建基于VsCode的ESP32的开发环境教程
  • PTA-根据已有类Worker,使用LinkedList编写一个WorkerList类,实现计算所有工人总工资的功能。
  • “候选对话链”(Candidate Dialogue Chain)概念
  • DAY18C语言笔记
  • Odoo 中SCSS的使用指南
  • AR/MR实时光照阴影开发教程
  • VSCODE的终端无法执行npm命令
  • rsync服务的搭建
  • vue-10( 动态路由匹配和路由参数)
  • 标准精读:2025 《可信数据空间 技术架构》【附全文阅读】
  • 机器学习:逻辑回归与混淆矩阵
  • [蓝桥杯]缩位求和
  • 李臻20242817_安全文件传输系统项目报告_第14周
  • Axure组件即拖即用:横向拖动菜单(支持左右拖动选中交互)
  • 8088单板机地址映射表