实用指南:【JavaEE】多线程案例(一)

news/2025/9/25 19:04:19/文章来源:https://www.cnblogs.com/slgkaifa/p/19111802

实用指南:【JavaEE】多线程案例(一)

 1.单例模式

    单例模式就是在设计某个程序时,某个类只能实例一次对象,如果再次实例一个对象就会报错。然而我们人类有时也是会出错的,只有机器是固定的,我们就是通过编译器编写一个强制要求的代码实现单例模式。

单例模式又分两种,一种是饿汉模式,另一种是懒汉模式。

1.1饿汉模式

饿汉模式是通过方法得到已创建的实例对象,而实例对象是在类加载时创建的(比较急切的想要)。

class Singleton{
private static Singleton singleton = new Singleton(); //将其实例对象设置为私有,并设置为静态变量,以方便别的成员调用
public static Singleton getSingleton(){//通过方法获得已创建的对象
return singleton;
}
private Singleton(){//将调用方法设置为私有,为了防止重新实例对象(重新实例会报错),
}
}

1.2懒汉模式

懒汉模式是在第一次使用时创建的(比较从容,用到了再创建)。

针对这两个模式分析,如果都再多线程条件下是否线程安全?

饿汉模式安全,懒汉不安全。之前讲述的线程安全问题出现的情况有:多个线程同时修改一个变量等。何况懒汉模式中还涉及到读取和修改,更容易被多线程穿插【在判断是否为空时,先读取后存在并执行另一个线程,这样就是穿插,如下图】。解决线程安全问题的主要做法就是:加锁

加锁就是要阻止存在穿插行为,既然如此,那我们直接在判断是否为空前加锁,如下图:

我们在深入研究,又出现了新的问题。我们实现懒汉模式只需要创建一次对象,而我们为了防止第一次创建而加锁导致后面的每一次调用对象都需要加锁,然而加锁就会出现锁冲突,锁冲突就容易导致阻塞等待。解决这个问题只需要添加 if 从句,判断是否是第一次创建。

指令重排序:编译器为了提高效率,可能会调整代码顺序(前提是保证逻辑不变)。

那这样就没有问题了吗?答:在创建对象的过程中可能出现指令重排序。new操作可以拆分成三步,第一步:申请空间;第二步:在内存空间上构造对象(构造方法);第三步:把内存的地址赋值给new的对象singlelazy。指令重排序可能将顺序调整为1 3 2,这样在单线程也是可以执行的。就是如果是多线程下,可能就会出现问题,比如:线程一执行到new操作,指令重排序将顺序调整后,先执行第一步(必须先有空间才能之后后面操作),再执行第三步将内存地址赋值给singlelazy ,此时的内存中是一个还没有初始化的非法对象。然后线程二开始执行,此时线程二中的singlelazy是非空的并且内存地址是没初始化的,然后 if 从句不满足直接跳出,最后将没有初始化的地址给了线程二中创建对象的变量,此时它指向的内存空间是非法的就出现问题了。

针对这个问题,我们可以用到volatile【之前提到的内存可见性也是编译器为了优化】。以下是懒汉模式单例模式的代码(此代码如果仔细研究还是存在些问题,正常下使用也是可以的):

class Singletonlazy{
private static volatile Singletonlazy singletonlazy = null;//先将变量置为空
public static Singletonlazy getSingletonlayz(){
if (singletonlazy == null) {
synchronized (Singletonlazy.class) {
if (singletonlazy == null) {
return singletonlazy = new Singletonlazy();
}
}
}
return singletonlazy;
}
private Singletonlazy(){
}
}

2.阻塞队列

阻塞队列:

1.线程安全

2.带有阻塞特性:

(a) 如果队列为空,继续出队列就会发生阻塞,阻塞到其他线程往队列里添加元素为止;

(b) 如果队列为满,继续入队列就会发生阻塞,阻塞到其他线程从队列中取走元素为止;

阻塞队列最大的用处就是用来实现“生产者消费者模型”;

生产者消费者模型

生产者负责生产物品,而消费者负责消耗东西。当生产者生产过快时就可以休息会;相反当生产者生产过慢时消费者就需要等待生产者生产物品。

生产者消费者模型意义
1.解耦合

两个模块,联系越大耦合越高。对分布式系统来说,解耦合的意义是很大的。

2.削峰填谷 

短时间内收到大量请求,服务器会把大量的请求写入阻塞队列中,其他服务器则正常从阻塞队列中获取请求。

实现方式

在Java标准库里已经提供了阻塞队列,我们直接使用即可,但是身为未来的程序员,我们还是需要知道它的底层实现原理并能熟习。

我们先讲标准库里的:BlockingQueue既能基于数组又能基于链表的,它是继承自Queue,所以可以使用Queue中的各种方法,但是这些都不具备 “阻塞” 的特性。put:阻塞式入队列;take:阻塞式出队列;

现在是我们自己实现的阻塞队列

其实很简单,我们只需要在普通的队列中加上线程安全以及阻塞即可,一个普通的队列怎么实现基于数组又基于链表,那就需要拿出我们的环形队列。

环形队列

想要实现阻塞功能,可以通过wait 、notify 实现,put 被阻塞等待 take 来唤醒,而 take 被阻塞等待 put 来唤醒。得到以下代码:

这个代码还有一个问题,就是当 put 方法因为队列满了,进入 wait 之后,wait 被唤醒时队列不一定时不满的。interrupt 方法是可以中断 wait 方法的,使用 interrupt 唤醒时会出现 InterrupttedException ,这个异常就是 wait 抛出的,我们使用 throws 是没有问题的,但是如果使用 try/catch 是有问题的,如果出现异常就直接往下进行了,接着就会把 tail 指向的元素覆盖掉,就相当于把一个有效的元素删掉了,并且此时队列还是满的,size 还会继续增加,后续还有继续put 。所以在使用 wait 时一定要注意是 notify 唤醒的还是 interrupt 唤醒的

针对这个问题如何解决呢?可以在 wait 结束后再判断是否 size 超过最大限制。

执行完下一个 if 后又面临同样的问题【interrupt唤醒还是notify唤醒】,那就接着 if ,如此一来就成了死循环了,不如直接用 while 实现知道队列不满。

在这过程中,size 、head 、tail 这三个变量不断的读取、修改,为了防止内存可见性,我们都给它添加一个 volatile 。以下就是阻塞队列实现的全部代码:

class MyBlockingQueue{
private String[] queue = new String[20];
private volatile int size;//这里选择通过一个变量来记录元素个数
private volatile int head;//头部
private volatile int tail;//尾部
private final Object lock = new Object();
public void put(String elem) throws InterruptedException {
synchronized (lock) {
while (size == queue.length) {
lock.wait();
}
queue[tail] = elem;
size++;
tail++;
if (tail == queue.length) {
tail = 0;
}
lock.notify();
}
}
public String take() throws InterruptedException {
synchronized (lock) {
while (size == 0) {
lock.wait();
}
String temp = queue[head];
size--;
head++;
if (head == queue.length) {
head = 0;
}
lock.notify();
return temp;
}
}
}

接着我们针对这个阻塞队列实现一个生产者消费者模型:

这里我们只实现 生产者较慢的情况,剩下的生产者快就是将消费者速度降低(直接在消费者中添加sheep可以实现),代码及结果如下:

生产者生产一个就消费者消费一个,下面是生产多个的结果

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

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

相关文章

网站开发和c语言反网站搭建一条龙

MySQL 5.5以上版本 与之前的版本安装出入有些区别: 下面是安装过程mysql5.6 下载地址:ftp://mirror.switch.ch/mirror/mysql/Downloads/MySQL-5.6/一:卸载旧版本使用下面的命令检查是否安装有MySQL Serverrpm -qa | grep mysql有的话通过下面…

架构架构设计师备考第32天——数据库交互NoSQL

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

交互:在终端中输入用户信息

交互:在终端中输入用户信息Python可以允许用户在终端中输入一些信息。 Input功能 接受输入字符串 # -*- coding: utf-8 -*- name = input("Please input your name:") print("Hello " + name + &…

传奇三端互通新开服网站百度非企推广开户

这个算法来自LINUX的源码,下面带有大神的解析,自己在RTC实验中也使用了,不用月份表,润平年的处理,几行就可得出结果,以下是程序和大神的解析Linux源码中的mktime算法解析我们知道,从CMOS中读出来…

php网站开发系统wordpress 引用网页

近日,紫光云技术有限公司在天津举行主题为“产业城市 擎领未来”的IMPACT2019紫光云峰会,深度阐释打造产业数字引擎的理念和实践,并为unI X云创中心揭牌,发布紫光云芯片产业数字引擎。 天津市人民政府副秘书长杨明远为大会致辞会上…

爱站网关键字挖掘wordpress 小组

Java中new一个对象时,JVM到底做了什么? 在Java编程中,new关键字是我们创建对象的最常用方式。但你是否想过,当你写下new MyClass()时,Java虚拟机(JVM)到底在背后做了哪些工作?今天&…

电脑迁移技巧:适用于 Windows 10/11 的免费磁盘克隆优秀的工具

电脑迁移技巧:适用于 Windows 10/11 的免费磁盘克隆优秀的工具pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Co…

详细介绍:Windows安装PostgreSQL入门操作手册

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

主播网站建立商务网站管理的主要内容数据管理

最近在微调 ChatGLM3-6b 时,训练好模型之后,调用inference_hf.py函数验证模型的时候报了如下错误,下面是解决方案。 我在训练时使用的是ptuning_v2.yaml配置文件,训练运行代码如下: CUDA_VISIBLE_DEVICES1 python fi…

东莞市公租房申请网站-建设网微信积分商城

Apache Doris 在查询优化方面通过结合 RBO 和 CBO,实现了对简单和复杂查询的高效优化。RBO 负责处理常量折叠、子查询改写和谓词下推等基础优化操作,而 CBO 则在 Join Reorder 等复杂场景中发挥作用。这种结合策略使得 Apache Doris 能够在面对各种查询场景时,既能保证优化过…

Java学习日记9.18

9.18 数据类型 整数扩展 进制 二进制: ob 十进制: 没有 八进制: 0 十六进制: 0x int i = 10 int i1 = 010 int i2 = 0x10输出结果将会是 10 8 16浮点数拓展 float 定义的小数必须在后边加一个f或F (因为小数默认的…

在PVE中实现宿主机与虚拟机同网段通信的配置方案

在PVE中实现宿主机与虚拟机同网段通信的配置方案本文内容由笔者根据遇到的问题口述,并通过ai整理而成供,遇到同样问题的同学参考。问题背景 在配置PVE服务器的过程中发现,当网络环境使用VLAN时,如果PVE宿主机的管理…

一种CDN动态加速首次访问加速方法

本文分享自天翼云开发者社区《一种CDN动态加速首次访问加速方法》.作者:蒋辉 具体方案如下: 1. 对于全站加速,节点内部的探测采用的非请求触发式探测(已实现),在首次访问时,使用配置的顶层父方案作为回源节点回源…

CF1716题解

CF1716A 不难发现,只保留一个1即可,其余的怎么变都可以,所以变成k个后,直接取max在序列中有1的情况下必然可以构造出来点击查看代码 #include<bits/stdc++.h> using namespace std; const int N=55; int t,n…

使用vosk模型进行语音识别

模型分享: 通过网盘分享的文件:vosk-model-small-cn-0.22.zip链接: https://pan.baidu.com/s/1FEH1xwDucdC3cEZSAyDOwQ?pwd=k8p5 提取码: k8p5 通过网盘分享的文件:vosk-model-cn-0.22.zip链接: https://pan.baidu…

国外 网站设计中国住房和城乡建设部网站资质查询

文章目录 前言一、技术是推动社会发展的基本动力1.人为什么能成为万物之长呢&#xff1f;2.人为什么要发明工具&#xff0c;进行进化呢&#xff1f;3.人是如何发明工具的&#xff1f;4.为什么要有不同的岗位和行业&#xff1f; 二、计算机(操作系统)发展的基本脉络1.第一台计算…

网站做实名验证成都广告设计培训班

世界上只有一种英雄主义&#xff0c;就是看清生活的真相之后依然热爱生活。对于 Lombok 我相信大部分人都不陌生&#xff0c;但对于它的实现原理以及缺点却鲜为人知&#xff0c;而本文将会从 Lombok 的原理出发&#xff0c;手撸一个简易版的 Lombok&#xff0c;让你理解这个热门…

AI Agent如何重塑人力资源管理?易路iBuilder平台实战案例深度解析

来源|商学院9月刊 文|吕笑颜 ID | BMR2004 导读:本文深度报道了易路iBuilder作为国内首个HR垂直AI Agent操作系统,如何通过39个智能体全面重构人力资源管理价值链。文章基于对易路CEO王天扬的专访,系统阐述了iBui…

docker-compose + macvlan + Elasticsearch - 9.1.4 + Kibana - 9.1.4

操作过程创建工作目录mkdir -p /opt/porsCloud/24-elastic进入工作目录cd /opt/porsCloud/24-elastic创建必要的目录mkdir es_conf es_data es_logs es_plugins kibana_conf kibana_data赋予权限chown -R 1000:0 es_co…

WinForm 计时器 Timer 学习笔记

1. Timer 是干啥的? 就是个“自动闹钟”: 你设定每隔多少毫秒(比如 1000 毫秒 = 1 秒),它就自动执行一段代码。 常见用途:实时显示时间 倒计时 定时刷新数据 轮询状态2. 怎么用?三步搞定(WinForm 拖控件方式)…