多线程案例(3) - 定时器,线程池

一,定时器

定时器作用:约定一个时间间隔,时间到达后,执行某段代码逻辑。实际上就是一个 "闹钟" 。

1.1使用标准库中的定时器

  • 标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .
  • Timer 类中含有一个扫描线程,观察是否有任务到达执行时间
  • schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒)
  • TimerTask 类继承了 Runnable 接口,所以能重写 run() 方法 
public class Test {public static void main(String[] args) {Timer timer = new Timer();timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("1111");}},1000);}
}

这是因为 Timer 内部的线程阻止了 进程 的结束,在 Timer 中是可以安排多个任务的,我们下面实现的时候要注意这一点。

1.2 定时器的实现

1. Timer 中需要一个扫描线程,来扫描任务是否到时间,可否执行。

2. Timer 可以安排多个任务执行,而每个任务的执行时间又不一样,所以我们需要使用优先级队列来存储任务,让这些任务按时间顺序排列。

3. 还需要创建一个类,通过类来描述一个任务,(包含任务的内容和时间)

class MyTimer{private PriorityQueue<MyTimerTask> priorityQueue = new PriorityQueue<>();public MyTimer(){//扫描线程Thread t = new Thread(() -> {while(true){synchronized (this){//涉及到修改操作,加锁try{while(priorityQueue.isEmpty()){this.wait();}MyTimerTask myTimerTask = priorityQueue.peek();long curTime = System.currentTimeMillis();//得到当前时间if(curTime >= myTimerTask.getTime()){//到达执行时间myTimerTask.getRunnable().run();priorityQueue.poll();}else {//未到达执行时间this.wait(myTimerTask.getTime() - curTime);//线程等待//如果没有这句代码,就会出现忙等,类似于,一直在看表}}catch (InterruptedException e){e.printStackTrace();}}}});t.start();}public void schedule(Runnable runnable, long delay){synchronized (this){MyTimerTask myTimerTask = new MyTimerTask(runnable,delay);priorityQueue.offer(myTimerTask);//将任务放入队列this.notify();//如果当前队列为空,唤醒线程}}
}class MyTimerTask implements Comparable<MyTimerTask>{private Runnable runnable;//任务内容private long time;//任务执行的具体时间public MyTimerTask(Runnable runnable, long delay){this.time = System.currentTimeMillis() + delay;this.runnable = runnable;}//得到任务执行的时间public long getTime(){return time;}//得到任务内容public Runnable getRunnable() {return runnable;}//重写比较方法,按照时间顺序从小到大排列@Overridepublic int compareTo(MyTimerTask o) {return (int) (this.time - o.time);}
}

关于上述代码还有几个细节需要注意:

1. 

因为 wait() 也可能会被InterruptedException打断,如果使用 if ,这时候队列仍然为null,就不能出现错误。

2.

因为如果使用sleep,有一种场景是不能成立的,就是当我们插入一个执行时间更早的任务时,线程还是处于休眠状态,这时候新插入的任务就会延迟执行,这不符合我们的逻辑。

而使用 wait 的话,线程就会重新去找那个最先执行的任务。

二,线程池

线程池能够减少线程创建和销毁的开销,也就是说适用于线程频繁创建销毁的场景。

2.1 使用标准库中的线程池

Executors 创建线程池的几种方式

  • newFixedThreadPool: 创建固定线程数的线程池
  • newCachedThreadPool: 创建线程数目动态增长的线程池,线程执行完后,不会立即销毁,而是会缓存一段时间
  • newSingleThreadExecutor: 创建只包含单个线程的线程池.
  • newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
public class Demo {public static void main(String[] args) {ExecutorService service = Executors.newCachedThreadPool();ExecutorService service1 = Executors.newFixedThreadPool(3);ExecutorService service2 = Executors.newSingleThreadExecutor();ExecutorService service3 = Executors.newScheduledThreadPool(2);service.submit(new Runnable() {//通过 ExecutorService.submit 可以注册一个任务到线程池@Overridepublic void run() {System.out.println("111");}});}
}

这里为什么不使用 new 的方法来创建一个线程池,而是使用 "工厂模式" 来实现呢?

首先了解一下什么是工厂模式,工厂模式是指使用普通方法来代替构造方法完成初始化工作,因为普通方法可以通过方法名来区分,也就不用受到重载规则的限制。比如:我们的坐标既可以使用笛卡尔坐标系,也可以使用极坐标系,这两个构造方法的参数是完全相同的,这时候我们就要使用 "工厂模式" 来初始化。

Executors 本质上是 ThreadPoolExecutor 类的封装,ThreadPoolExecutor 提供了更多的可选参数, 可以进一步细化线程池行为的设定。下面我们来介绍一下构造方法的参数(很重要!!!)。

  • corePoolSize : 线程池最少有多少线程
  • maximumPoolSize : 线程池最多有多少线程
  • keepAliverTime : 线程有多长的 "摸鱼" 时间,如果一个线程有 keepAliverTime 个时间没有工作,那么就会销毁该线程。
  • unit : keepAliverTime 的单位
  • workQueue :阻塞队列,如果需要优先级,就设置 PriorityBlockingQueue,如果有数量限制,就设置 ArrayBlockingQueue,如果数目变动较大,就设置 LinkedBlockingQueue
  • threadFactory :工厂模式,使用工厂模式来创建线程,设置一些线程的属性
  • handler :线程池的拒绝策略,一个线程池容纳的任务数量是有上限的,当到达上限后,继续添加线程的处理方式,处理的4种方式如下:

 这里还有一道经典的面试题:如果使用线程池需要设定线程的数目,设置成多少合适?

这个时候,只要回答出具体的数字就错的,因为一个线程执行的代码有两类:

1)cpu 密集型:代码主要进行算术运算 / 逻辑运算

2)IO 密集型:代码主要进行 IO 操作

假设一个线程的所有代码都是 cpu 密集型,这时候线程池的线程数量不应该超过 N (cpu 逻辑核心数),大于 N,也无法提高效率。

假设一个线程的所有代码都是 IO 密集型,这时候不吃 cpu,这时候就可以超过 N.

代码不同,一个线程池的线程数量设置就不同,因为我们无法知道一段代码,有多少是cpu密集型,多少是 IO 密集型。正确的回答是:使用实验的方式,对程序进行性能测试,在测试过程中不段的调整线程池的线程数量,看哪种情况更符合要求。

 2.2 线程池的简单实现

import java.util.concurrent.*;class MyThreadPool{BlockingQueue<Runnable> blockingQueue = new ArrayBlockingQueue<>(4);public void submit(Runnable runnable){try {blockingQueue.put(runnable);} catch (InterruptedException e) {throw new RuntimeException(e);}}public MyThreadPool(int n){for (int i = 0; i < n; i++) {Thread t = new Thread(()->{try {Runnable a = blockingQueue.take();a.run();} catch (InterruptedException e) {throw new RuntimeException(e);}});t.start();}}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/80288.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

element-ui文件下载(单个)

1. 单个附件下载 <el-buttontype"text"size"small"click.native.prevent"download(scope.row)" >下载</el-button>export default {data() {return {downloadUrl: http://127.0.0.1:8881/XX/XX, // 下载接口}},methods: {download(…

国庆中秋特辑(一)浪漫祝福方式 用循环神经网络(RNN)或长短时记忆网络(LSTM)生成祝福诗词

目录 一、使用深度学习中的循环神经网络&#xff08;RNN&#xff09;或长短时记忆网络&#xff08;LSTM&#xff09;生成诗词二、优化&#xff1a;使用双向 LSTM 或 GRU 单元来更好地捕捉上下文信息三、优化&#xff1a;使用生成对抗网络&#xff08;GAN&#xff09;或其他技术…

YOLOV7改进-添加基于注意力机制的目标检测头(DYHEAD)

DYHEAD 复制到这&#xff1a; 1、models下新建文件 2、yolo.py中import一下 3、改IDetect这里 4、论文中说6的效果最好&#xff0c;但参数量不少&#xff0c;做一下工作量 5、在进入IDetect之前&#xff0c;会对RepConv做卷积 5、因为DYHEAD需要三个层输入的特征层一致&am…

Jetpack Compose 介绍和快速上手

Compose版本发展 19年&#xff0c;Compose在Google IO大会横空出世&#xff0c;大家都议论纷纷&#xff0c;为其前途堪忧。 21年7月Compose 1.0的正式发布&#xff0c;却让大家看到了Google在推广Compose上的坚决&#xff0c;这也注定Compose会成为UI开发的新风向。 23年1月…

can‘t sync to target.

飞翔仿真器 无法 与S12单片机 建立联系&#xff0c;仿真时显示 cant sync to target. 但是使用仿真器与其他板子连接仿真是没问题的。 首先怀疑硬件问题&#xff1a;没发现问题&#xff1b; 然后&#xff0c;勇敢的点击菜单中 设置速度&#xff0c;根据自己晶振和建议设置如…

套接字通信之 端口

端口 端口的本质? 无符号短整型数-> unsigned short端口取值范围? 可以有多少个端口? 2的16次方取值范围:0 - 65535 端口的作用? 定位某台主机上运行的某个进程 在电脑上运行了微信和QQ&#xff0c;小明给我的的微信发消息&#xff0c;电脑上的微信就收到了消息&#…

C语言经典100例题(56-60)--画圆;画方;画线

目录 【程序56】题目&#xff1a;画图&#xff0c;学用circle画圆形 【程序57】题目&#xff1a;画图&#xff0c;学用line画直线。 【程序58】题目&#xff1a;画图&#xff0c;学用rectangle画方形。 【程序59】题目&#xff1a;画图&#xff0c;综合例子。 【程序60】题…

arcgis拓扑检查实现多个矢量数据之间消除重叠区域

目录 环境介绍&#xff1a; 操作任务&#xff1a; 步骤&#xff1a; 1、数据库和文件结构准备 2、建立拓扑规则 3、一直下一页默认参数后&#xff0c;进行拓扑检查 4、打开TP_CK_Topology&#xff0c;会自动带出拓扑要素&#xff0c;红色区域为拓扑错误的地方&#xff1…

upload-labs文件上传靶场实操

文章目录 1.Pass-012.Pass-023.Pass-034.Pass-045.Pass-056.Pass-067.Pass-078.Pass-089.Pass-0910.Pass-1011.Pass-1112.Pass-1213.Pass-1314.Pass-1415.Pass-1516.Pass-1617.Pass-1718.Pass-1819.Pass-1920.Pass-20 上传姿势总结&#xff1a; 1)改后缀名绕过 2)Content-Type绕…

flink-1.14.4启动报错setPreferCheckpointForRecovery(Z)v

从flink1.12升级到flink1.14&#xff0c;修改了pom.xml的flink-version&#xff0c;打包的时候发现报错&#xff1a; // 当有较新的 Savepoint 时&#xff0c;作业也会从 Checkpoint 处恢复env.getCheckpointConfig().setPreferCheckpointForRecovery(true); 于是屏蔽了这段配置…

微信小程序怎么隐藏顶部导航栏(navigationBar)变透明的解决方案

怎么隐藏小程序顶部导航栏&#xff08;navigationBar&#xff09;&#xff1f; 官网说&#xff1a;Navigation是小程序的顶部导航组件&#xff0c;当页面配置 navigationStyle 设置为 custom 的时候可以使用此组件替代原生导航栏。 那么&#xff0c;我们就知道这种效果是可以…

SpringMVC多文件上传

文章目录 一、文件上传1.1 导入pom依赖1.2 配置文件上传解析器1.3 设置文件上传表单1.4 实现文件上传 二、文件下载三、多文件上传四、JRebel的使用 一、文件上传 1.1 导入pom依赖 <commons-fileupload.version>1.3.3</commons-fileupload.version><dependency…

abortControllerMap: Map<string, AbortController>

abortControllerMap: Map&#xff1c;string, AbortController&#xff1e;AbortController 是一个用于控制和取消异步任务的接口。 在这里&#xff0c;AbortController 用作一个映射的值&#xff0c;与映射的键&#xff08;string 类型&#xff09;相关联。 AbortController 可…

redis设计规范

部分内容参考&#xff1a;阿里redis开发规范 同时&#xff0c;结合shigen在实习中的实践经验总结。 key的名称设计 可读性和管理性 业务名: 表名: id pro:user:1001简洁性 控制key的长度&#xff0c;可以用缩写 transaction -> tras拒绝bigkey 防止网卡流量、慢查询&…

React Native 环境配置(mac)

React Native 环境配置&#xff08;mac&#xff09; 1.Homebrew2.Node.js、WatchMan3.Yarn4.Android环境配置1.安装JDK2.下载AndroidStudio1.国内配置 Http Proxy2.安装SDK1.首先配置sdk的路径2.SDK 下载 3.创建模拟器4.配置 ANDROID_HOME 环境变量 5.IOS环境1.升级ruby&#x…

学校项目培训之Carla仿真平台之安装Carla

官网&#xff1a;http://carla.org/ 写在前面 由于安装都写了很多东西&#xff0c;所以我单独将安装弄出来记录一下。 如果你在安装9.12版本的时候遇到了很多问题&#xff0c;你可以考虑以下几点&#xff1a; - 楼梯可能不太行&#xff0c;需要更换&#xff0c;这是我实践得到的…

浅析Java责任链模式实现

一、概要 定义&#xff1a;责任链模式是一种行为设计模式&#xff0c; 允许你将请求沿着处理者链进行发送。收到请求后&#xff0c; 每个处理者均可对请求进行处理&#xff0c; 或将其传递给链上的下个处理者。 二、应用场景&#xff1a; 1.多条件流程判断&#xff1a;权限相关…

iOS开发之编译OpenSSL静态库

项目审查发现OpenSSL1.0.2d有漏洞&#xff0c;所以需要升级更新OpenSSL版本&#xff0c;借此机会&#xff0c;记录一下编译OpenSSL静态库的流程。 Xcode使用的是14.2&#xff0c;OpenSSL使用的是1.0.2u、1.1.1u&#xff0c;由于是对两个不同版本进行的编译操作&#xff0c;所以…

Linux服务器部署常用命令记录【持续更新】

介绍&#xff1a;最近服务器被人频繁攻击&#xff0c;留下一堆垃圾文件。重装后需要重新部署&#xff0c;才发现Linux的命令怎么这么碎。于是乎就产生了写这篇文章的想法。本文旨在记录常用的Linux部署需要使用的命令&#xff0c;另一篇关于Linux docker安装常用软件的文章&…

HTTP响应详解, HTTP请求构造及HTTPS详解

HTTP响应详解 认识 "状态码" (status code) 状态码表示访问一个页面的结果 . ( 是访问成功 , 还是失败 , 还是其他的一些情况 ...). 以下为常见的状态码 . 200 OK 这 是一个最常见的状态码, 表示访问成功 . 抓包抓到的大部分结果都是 200 例如访问搜狗…