多线程案例

多线程案例

  • 1. 单例模式
  • 2. 阻塞式队列
  • 3. 定时器
  • 4. 线程池

1. 单例模式

单例模式能保证某个类在程序中只存在唯一一份实例, 而不会创建出多个实例.分为懒汉式和饿汉式两种

  1. 饿汉式: 类加载的同时, 创建实例.
class Singleton {private static Singleton instance = new Singleton();private Singleton() {}public static Singleton getInstance() {return instance;}
}
  1. 饿汉式: 类加载的时候不创建实例. 第一次使用的时候才创建实例.
class Singleton {private static Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {instance = new Singleton();}return instance;}
}

显而易见, 上述代码如果在单线程情况下没有问题, 但是在多线程情况下就有可能发生线程安全问题. 如果在多个线程中同时调用 getInstance 方法, 就可能导致创建出多个实例.
所以就需要对上述代码进行改进:

class Singleton {private static volatile Singleton instance = null;private Singleton() {}public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton();}}}return instance;}
}

2. 阻塞式队列

阻塞队列是一种特殊的队列. 也遵守 “先进先出” 的原则. 是一种线程安全的数据结构, 并且具有以下特性:
当队列满的时候, 继续入队列就会阻塞, 直到有其他线程从队列中取走元素.
当队列空的时候, 继续出队列也会阻塞, 直到有其他线程往队列中插入元素.
阻塞队列的一个典型应用场景就是 “生产者消费者模型”. 这是一种非常典型的开发模型.

在 Java 标准库中内置了阻塞队列. 如果我们需要在一些程序中使用阻塞队列, 直接使用标准库中的即可.
BlockingQueue 是一个接口. 真正实现的类是 LinkedBlockingQueue.
put 方法用于阻塞式的入队列, take 用于阻塞式的出队列.
BlockingQueue 也有 offer, poll, peek 等方法, 但是这些方法不带有阻塞特性.

BlockingQueue<String> queue = new LinkedBlockingQueue<>();
// 入队列
queue.put("abc");
// 出队列. 如果没有 put 直接 take, 就会阻塞. 
String elem = queue.take();

代码实现:

class MyBlockingDeque {private int[] items = new int[1000];private int head = 0;private int tail = 0;private int size = 0;public void put(int val) throws InterruptedException {synchronized (this) {while (size == items.length) {this.wait();}items[tail] = val;tail++;size++;if (tail >= items.length) {tail = 0;}this.notify();}}public Integer take() throws InterruptedException {int rest = 0;synchronized (this) {while (size == 0) {this.wait();}rest = items[head];head++;size--;if (head >= items.length) {head = 0;}this.notify();}return rest;}
}

3. 定时器

定时器也是软件开发中的一个重要组件. 类似于一个 “闹钟”. 达到一个设定的时间之后, 就执行某个指定好的代码.

标准库中提供了一个 Timer 类. Timer 类的核心方法为 schedule .
schedule 包含两个参数. 第一个参数指定即将要执行的任务代码, 第二个参数指定多长时间之后执行 (单位为毫秒).

Timer timer = new Timer();
timer.schedule(new TimerTask() {@Overridepublic void run() {System.out.println("hello");}
}, 3000);

定时器的构成:

  1. 一个带优先级的阻塞队列
    为啥要带优先级呢?
    因为阻塞队列中的任务都有各自的执行时刻 (delay). 最先执行的任务一定是 delay 最小的. 使用带优先级的队列就可以高效的把这个 delay 最小的任务找出来.

  2. 队列中的每个元素是一个 Task 对象.

  3. Task 中带有一个时间属性, 队首元素就是即将

  4. 同时有一个 worker 线程一直扫描队首元素, 看队首元素是否需要执行

  5. Task 类用于描述一个任务(作为 Timer 的内部类). 里面包含一个 Runnable 对象和一个 time(毫秒时间戳),这个对象需要放到 优先队列 中. 因此需要实现 Comparable 接口.

class MyTake implements Comparable<MyTake> {private Runnable runnable;private long time;public MyTake(Runnable runnable, long time) {this.runnable = runnable;this.time = time;}public long getTime() {return time;}public void run() {runnable.run();}@Overridepublic int compareTo(MyTake o) {return (int) (this.time - o.time);}
}
  1. Timer 类 是主体部分
 private Thread t = null;public MyTimer() {t = new Thread(() -> {while (true) {try {synchronized (this) {MyTake myTake = queue.take();long cur = System.currentTimeMillis();if (cur < myTake.getTime()) {queue.put(myTake);this.wait(myTake.getTime() - cur);//while 一直转会消耗资源所以可以使用wait使线程阻塞}else {myTake.run();}}} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();}PriorityBlockingQueue<MyTake> queue = new PriorityBlockingQueue<>();public void schedule(Runnable runnable, int after) {MyTake myTake = new MyTake(runnable,System.currentTimeMillis() + after);queue.put(myTake);synchronized (this) {this.notify();//因为插入的任务等待时间可能比之前堆顶元素的时间早所以每次插入需要唤醒线程}}

4. 线程池

标准库中的线程池
使用 Executors.newFixedThreadPool(10) 能创建出固定包含 10 个线程的线程池.
返回值类型为 ExecutorService
通过 ExecutorService.submit 可以注册一个任务到线程池中.

ExecutorService pool = Executors.newFixedThreadPool(10);
pool.submit(new Runnable() {@Overridepublic void run() {System.out.println("hello");}
});

Executors 创建线程池的几种方式
newFixedThreadPool: 创建固定线程数的线程池
newCachedThreadPool: 创建线程数目动态增长的线程池.
newSingleThreadExecutor: 创建只包含单个线程的线程池.
newScheduledThreadPool: 设定 延迟时间后执行命令,或者定期执行命令. 是进阶版的 Timer.
Executors 本质上是 ThreadPoolExecutor 类的封装.
代码实现:

private BlockingDeque<Runnable> blockingDeque = new LinkedBlockingDeque<>();public MyThreadPool(int n) {for (int i = 0; i < n; i++) {Thread t = new Thread(() -> {while (true) {Runnable run = null;try {run = blockingDeque.take();} catch (InterruptedException e) {throw new RuntimeException(e);}run.run();}});t.start();}}public void submit(Runnable runnable) {try {blockingDeque.put(runnable);} catch (InterruptedException e) {throw new RuntimeException(e);}}

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

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

相关文章

计算机网络——网络层

文章目录 **1 网络层的功能****1.1 异构网络互连****1.2 路由与转发****1.3 SDN的基本概念****1.4 拥塞控制** **2 路由算法****2.1 静态路由和动态路由****2.2 距离-向量路由算法&#xff08;动态&#xff09;****2.3 链路状态路由算法&#xff08;动态&#xff09;****2.4 层…

技术速览|Meta Llama 2 下一代开源大型语言模型

AI 使用大型语言模型&#xff08;LLM&#xff09;来理解和生成自然语言。LLM 可以从大量文本中学习并创建有关各种主题的文本&#xff0c;并可以完成比如编写代码、生成歌词、总结文章等任务。但有些 LLM 相关课程成本高昂且封闭&#xff0c;而现有的开放课程数量十分有限。这就…

Python爬虫+数据可视化:分析唯品会商品数据

目录 前言数据来源分析1. 明确需求2. 抓包分析&#xff1a;通过浏览器自带工具: 开发者工具 代码实现步骤: 发送请求 -> 获取数据 -> 解析数据 -> 保存数据发送请求解析数据保存数据 数据可视化先读取数据泳衣商品性别占比商品品牌分布占比各大品牌商品售价平均价格各…

LINUX中的myaql(一)安装

目录 前言 一、概述 二、数据库类型 三、数据库模型 四、MYSQL的安装 &#xff08;一&#xff09;yum安装MYSQL &#xff08;二&#xff09;rpm安装MYSQL 五、MYSQL本地登录 rpm安装MYSQL本地登录 六、重置密码 总结 前言 MySQL是一种常用的开源关系型数据库管理系统&#xff…

MATLAB与ROS联合仿真——ROS环境搭建及相关准备工作(下)

本篇文章主要介绍在安装完ROS后&#xff0c;在进行MATLAB与ROS联合仿真之前&#xff0c;需要进行的一些环境搭建以及准备工作&#xff0c;主要分为 创建ROS工作空间及功能包、必备功能包安装、安装Gazebo11、导入实验功能包至工作空间、安装Visual_Studio_Code(选做)、常用便捷…

Mysql错误日志、通用查询日志、二进制日志和慢日志的介绍和查看

一.日志 1.日志和备份的必要性 日志刷新 2.mysql的日志类型 &#xff08;1&#xff09;错误日志 查看当前错误日志和是否记录警告设置 &#xff08;2&#xff09;通用查询日志 查看通用查询日志的设置 &#xff08;3&#xff09;二进制日志 查看二进制文件的设置&…

-Ddfs.checksum.combine.mode=COMPOSITE_CRC参数解析

-Ddfs.checksum.combine.modeCOMPOSITE_CRC -Ddfs.checksum.combine.modeCOMPOSITE_CRC是一个Hadoop配置参数&#xff0c;用于指定HDFS&#xff08;Hadoop分布式文件系统&#xff09;在计算文件校验和时使用的模式。 具体来说&#xff0c;dfs.checksum.combine.mode 参数决定…

最小二乘拟合椭圆

目录 1.拟合椭圆2.示例代码 爬虫网站自重。 1.拟合椭圆 二次曲线的一般方程为&#xff1a; A x 2 B x y C y 2 D x E y F 0 Ax^2BxyCy^2DxEyF0 Ax2BxyCy2DxEyF0 令&#xff1a; Δ B 2 − 4 A C Δ B^2-4AC ΔB2−4AC 那么&#xff0c;当 Δ > 0 Δ >0 Δ>0…

js基础-练习三

九九乘法表&#xff1a; <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthsc, initial-scale1.0"><title>九九乘法表</title><style&g…

NLP杂记

来京一周余&#xff0c;初病将愈&#xff0c;终跑通llama及ViT&#xff0c;记于此—— 之前都是做的图像&#xff0c;大模型迁移基本上都是NLP相关的知识&#xff0c;很多东西和CV差距还是有点&#xff0c;再加上大模型对算力要求较高&#xff0c;基于云的操作对我一个习惯在本…

【uniapp学习之】uni-forms必填项校验

代码块 <uni-forms ref"baseForm" :modelValue"baseFormData" label-widthauto :rules"rules"><uni-forms-item label"企业名称" required name"principalName"><uni-easyinput v-model"baseFormData.…

node中间件-koa框架

文章目录 1. koa 基本使用2. 参数解析3. 请求路径区分4. 路由5 参数解析5.1 params 与query解析5.2 body参数与urlencoded 解析5.3 form-data参数 6 . 文件上传7. 静态服务器8 响应数据9 错误处理 1. koa 基本使用 安装 npm i koakoa导出的是一个类&#xff0c;必须用new关键字…

Linux 用户组相关命令

添加用户组的命令是 groupadd&#xff0c;命令格式如下: [rootlocalhost ~]# groupadd [选项] 组名 选项&#xff1a; -g GID&#xff1a;指定组 ID&#xff1b;-r&#xff1a;创建系统群组。 使用 groupadd 命令创建新群组非常简单&#xff0c;例如&#xff1a; [rootlocalh…

Pytorch个人学习记录总结 05

目录 神经网络的基本骨架 卷积操作 torch.nn.functional.conv2d 神经网络的基本骨架 搭建Neural Network骨架主要用到的包是torch.nn&#xff0c;官方文档网址&#xff1a;torch.nn — PyTorch 2.0 documentation&#xff0c;其中torch.nn.Module很重要&#xff0c;是所有所…

springboot mybatis-plus 多数据源配置(HikariCP)

1.导入依赖jar <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><dependency><groupId>org.postgresql</groupId><artifactId>postgres…

【JVM】浅看JVM的运行流程和垃圾回收

1.JVM是什么 JVM&#xff08; Java Virtual Machine&#xff09;就是Java虚拟机。 Java的程序都运行在JVM中。 2.JVM的运行流程 JVM的执行流程&#xff1a; 程序在执行之前先要把java代码转换成字节码&#xff08;class文件&#xff09;&#xff0c;JVM 首先需要把字节码通过…

springboot中logback日志配置

springboot中logback日志配置 前言默认配置logback-spring.xml详细配置 前言 Spring Boot使用Apache的Commons Logging作为内部的日志框架&#xff0c;其仅仅是一个日志接口&#xff0c;在实际应用中需要为该接口来指定相应的日志实现。 Spring Boot从1.4版本开始内置的日志框…

程序员面试系列,kafka常见面试题

原文链接 Kafka是什么&#xff1f;它的主要作用是什么&#xff1f;什么是Kafka的主题&#xff08;Topic&#xff09;和分区&#xff08;Partition&#xff09;&#xff1f;Kafka中的消息是如何被生产者发送和消费者接收的&#xff1f;Kafka中的分区有什么作用&#xff1f;为什…

聊聊Linq中.AsEnumerable(), AsQueryable() ,.ToList(),的区别和用法

聊聊Linq中.AsEnumerable(), AsQueryable() ,.ToList(),的区别和用法 当使用LINQ查询数据时&#xff0c;我们常常会面临选择使用.AsEnumerable(), .AsQueryable(), 和 .ToList()方法的情况。这些方法在使用时有不同的效果和影响&#xff0c;需要根据具体场景来选择合适的方法。…

springboot热加载spring-boot-devtools:

springboot热加载 基于idea开发springboot项目使用热加载 pom依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</op…