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

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.创建新的线程 是需要“用户态+内核态”共同完成的 

内核态和用户态:

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

  

相关文章:

  • 【Java学习笔记】枚举
  • 初学大模型部署以及案例应用(windows+wsl+dify+mysql+Ollama+Xinference)
  • python打卡day42
  • Mask_RCNN 环境配置及训练
  • leetcode hot100 二叉树(一)
  • 第七部分:第四节 - 在 NestJS 应用中集成 MySQL (使用 TypeORM):结构化厨房的原材料管理系统
  • 剑指offer hot100 第三周
  • 查看make命令执行后涉及的预编译宏定义的值
  • java synchronized关键字用法
  • io流2——字节输入流,文件拷贝
  • Codeforces 1027 Div3(ABCDEF)
  • Java网络编程基础:从阻塞式I/O到线程池模型
  • DAY 34 超大力王爱学Python
  • C++ —— STL容器——string类
  • ps中通过拷贝的图层和通过剪切的图层
  • java多线程与JUC
  • ck-editor5的研究 (4):初步使用 CKEditor5 的插件功能
  • Cesium快速入门到精通系列教程三
  • 高速串行接口
  • Spring Boot 4.0实战:构建高并发电商系统
  • 医院网站建设的特点/推广产品的软文怎么写
  • 北京的网站建设公司有哪些/优化关键词排名seo软件
  • 网站 没有域名需要备案吗/seo综合查询中的具体内容有哪些
  • 免费域名网站推荐/哪里做网络推广
  • 网站建设论文3000字范文/营销策划公司排行榜
  • 集团网站开发多少钱/重庆官网seo分析