多线程案例(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.rhkb.cn/news/134064.html

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

相关文章

防止泄露,保护隐私!如何清除你的谷歌搜索历史记录

按照以下说明学习如何从你的谷歌帐户、谷歌Chrome浏览器、谷歌iOS或Android应用程序或谷歌应用程序中删除你的谷歌历史记录。 如何从你的谷歌帐户中删除搜索历史记录 清除你的谷歌搜索历史并不意味着谷歌实际上会删除你的搜索数据。谷歌仍然会记录你如何以及何时使用某些功能…

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

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

力扣236 补9.14

做不来&#xff0c;我做中等题基本上都是没有思路&#xff0c;这里需要先遍历祖先节点&#xff0c;那必然用先序遍历&#xff0c;这题还是官方题解容易理解&#xff0c;第二火的题解反而把我弄得脑袋昏昏的。 class Solution { TreeNode ans; public TreeNode lowestCommonAnce…

索引-动图演示存储过程

索引 二叉树存储过程演示 BThree存储过程 sql二级索引搜索过程 Id是唯一键&#xff0c;聚集索引 只存在一个 Name是二级索引 可以存在多个 第一种效率更高&#xff0c;不需要回表

数据治理的 “独孤九剑”

加gzh“大数据食铁兽”&#xff0c;了解更多大数据资讯&#xff01; 来源&#xff1a;与数据同行 免责声明&#xff1a;以上报告均系本平台通过公开、合法渠道获得&#xff0c;报告版权归原撰写/发布机构所有&#xff0c;如涉侵权&#xff0c;请联系删除 &#xff1b;资料…

浅谈C++|类的封装篇

引子&#xff1a; C认为万事万物皆为对象&#xff0c;对象有其属性和行为。 人可以作为对象&#xff0c;属性有姓名&#xff0c;年龄&#xff0c;身高&#xff0c;体重等&#xff0c;行为有行走&#xff0c;吃饭&#xff0c;唱歌等。 车也可以作为一个对象&#xff0c;属性有轮…

Jetpack Compose 介绍和快速上手

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

千里共婵娟 | 结合微信公众号用JavaScript完整开发实现换中秋头像的功能

&#x1f3c6;作者简介&#xff0c;黑夜开发者&#xff0c;CSDN领军人物&#xff0c;全栈领域优质创作者✌&#xff0c;阿里云社区专家博主&#xff0c;2023年6月csdn上海赛道top4。 &#x1f3c6;数年电商行业从业经验&#xff0c;历任核心研发工程师&#xff0c;项目技术负责…

uni-app运行到微信开发者工具-没有打印的情况

前言 到我们进场使用微信开发者工具时&#xff0c;就会发现它经常会有bug&#xff0c;特别是在软件更新&#xff0c;组件库更新之后 最近在更新微信开发者工具之后发现所有打印都不显示了&#xff0c;虽然是小问题-但对于强迫症很烦 以为是代码配置问题-结果是更新之后打印开…

制作ubuntu18.04系统盘

文章目录 前言一、下载ubuntu18.04的iso文件二、制作u盘系统盘2、使用ultra来制作系统盘2.1、加载iso2.2、制作系统盘 前言 安装ubuntu18.04系统 一、下载ubuntu18.04的iso文件 打开下面的网址&#xff0c;找到自己需要的iso文件 https://releases.ubuntu.com/二、制作u盘系…

任意区域的色彩一致性处理方法

影像任意感兴趣区域的色彩一致性处理方法&#xff0c;主要是针对掩膜后的影像&#xff0c;类似下图&#xff0c;对非背景区域的像素进行处理 其中非黑色部分我们叫待匀色区域。 这种处理 对于wallis 和直方图匹配 很容易实现&#xff0c;但是颜色转移就相对而言 困难点。 颜…

解决中国科大 USTC 邮箱系统的超大附件上传的邮箱控件安装问题

USTC邮箱系统上传超过 48M 的附件的步骤&#xff1a; 从文件中转站上传文件&#xff0c;会提示下载邮箱控件 cmplugin_setup.exe &#xff0c;默认安装C盘即可 2. 安装好之后依然无法上传超大文件&#xff0c;因为只有 IE 浏览器支持该功能&#xff0c;所以可以使用 Edge 浏览…

华为云云耀云服务器L实例评测|Git 私服搭建指南

前言 本文为华为云云耀云服务器L实例测评文章&#xff0c;测评内容是 云耀云服务器L实例 Git 私有服务器搭建指南 系统配置&#xff1a;2核2G 3M Ubuntu 20.04 我们平时在使用代码托管服务的时候&#xff0c;可能某些代码托管平台对成员有限制&#xff0c;或是由于内容原因会对…

Python中异常处理4-4

在Python中的异常处理4-1_棉猴的博客-CSDN博客中提到&#xff0c;在try块中的代码运行时如果出现异常&#xff0c;会自动抛出这个异常。可以通过raise语句手动抛出异常。 1 raise语句手动抛出异常 raise后面跟要抛出的异常类或者异常类的实例&#xff0c;表示手动抛出该异常&…

【基于Cocos Creator 3.5的赛车游戏】8.引入触摸屏幕事件并简单的控制小车

转载知识星球 | 深度连接铁杆粉丝&#xff0c;运营高品质社群&#xff0c;知识变现的工具 项目地址&#xff1a;赛车小游戏-基于Cocos Creator 3.5版本实现: 课程的源码&#xff0c;基于Cocos Creator 3.5版本实现 上一张您已经对Cocos的坐标系有了了解。这一章我们将让小车能…

Python __slots__:限制类实例动态添加属性和方法

​通过学习《Python类变量和实例变量》一节&#xff0c;了解了如何动态的为单个实例对象添加属性&#xff0c;甚至如果必要的话&#xff0c;还可以为所有的类实例对象统一添加属性&#xff08;通过给类添加属性&#xff09;。​ ​ 那么&#xff0c;Python 是 ​否也允许动态地…

MySQL 约束与复杂查询

当涉及到数据库管理系统&#xff08;DBMS&#xff09;的高级主题时&#xff0c;包括数据库的约束、表的设计以及各种类型的查询&#xff0c;特别是聚合查询、联合查询和合并查询&#xff0c;是非常重要的。这些主题可以帮助我们更好地理解数据库的内部工作机制以及如何有效地操…

二叉搜索树经典笔试题【力扣、牛客】

文章目录 1.根据二叉树创建字符串2. 二叉树的层序遍历3.二叉树的层序遍历Ⅱ4.二叉树的最近公共祖先1.法一&#xff1a;定位p、q在左还是右 分类讨论2.法二&#xff1a;利用stack求出p、q路径 求相交值 5.二叉搜索树与双向链表1.法一&#xff1a;递归&#xff1a;递归过程修正指…

Acwing 828. 模拟栈

Acwing 828. 模拟栈 题目要求思路讲解代码展示 题目要求 思路讲解 栈&#xff1a;先进后出 队列&#xff1a;先进先出 代码展示 #include <iostream>using namespace std;const int N 100010;int m; int stk[N], tt;int main() {cin >> m;while (m -- ){string o…

java创建excel文件和解析excel文件

创建excel文件 package com.bjpowernode.crm.poi;import org.apache.poi.hssf.usermodel.*; import org.apache.poi.ss.usermodel.HorizontalAlignment;import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.…