【JavaEE】多线程代码案例(1)

在这里插入图片描述
🎏🎏🎏个人主页🎏🎏🎏
🎏🎏🎏JavaEE专栏🎏🎏🎏
🎏🎏🎏上一篇文章:多线程(2)🎏🎏🎏

文章目录

  • 1.单例模型
    • 1.1概念
    • 1.2如何保证只有一个对象
    • 1.3两种单例模式
    • 1.4两种模式的线程安全
      • 1.4.1饿汉模式——线程安全
      • 1.4.2懒汉模式——线程不安全
    • 2.2.阻塞队列
    • 2.1概念
    • 2.2两种队列
      • 2.2.1阻塞队列
      • 2.2.2消息队列
    • 2.3模拟实现阻塞队列

1.单例模型

1.1概念

单例模型——》单个实例,在整个进程中某一个类只有一个实例化对象(不会new出来多个对象)称为单例模型。

1.2如何保证只有一个对象

靠我们程序猿本身来保证,这样肯定是不现实的,所以需要编译器来帮我们来做一个强制的检查,通过一些编码上的技巧,使编译器可以自动发现我们的代码是否有多个对象,并且在尝试创建多个实例的时候,直接编译出错。

1.3两种单例模式

  1. 饿汉模式
    在程序开始的时候就创建了对象,之后需要用到这个对象只需要调用这个对象即可,不需要再重新创建一个对象。为了防止创建出多个对象,可以利用代码的结构来操作比如创建以一个私有的构造方法。
//1.饿汉模式
class Singleton {private static Singleton instance = new Singleton();public static  Singleton getInstance() {return instance;}private Singleton() {}
}
public class Deom22 {public static void main(String[] args) {Singleton.getInstance();Singleton s1 = Singleton.getInstance();Singleton s2 = Singleton.getInstance();System.out.println(s1 == s2);}
}
  1. 懒汉模式
    相比饿汉模式的区别就是等程序需要用到这个对象的时候,才创建这个对象,这样操作的好处就是可以节省创建实例的成本以及避免一个项目中要用到多个单例模式,这样就要同时产生多个单个实例,会导致程序启动变慢。
//懒汉模式
class SinlgetonLazy {private static SinlgetonLazy instance = null;public static SinlgetonLazy getInstance() {if(instance == null) {instance = new SinlgetonLazy();}return instance;}private SinlgetonLazy() {}
}
public class Deom23 {public static void main(String[] args) {SinlgetonLazy s1 = SinlgetonLazy.getInstance();SinlgetonLazy s2 = SinlgetonLazy.getInstance();System.out.println(s1 == s2);}
}

1.4两种模式的线程安全

1.4.1饿汉模式——线程安全

原因:由于饿汉模式中的实例化对象是一开始就被创建了,比main线程调用还要早一些,其他线程比main线程还要慢,当其他线程来调用getInstance的时候,只是读取,并不是修改,多个线程同时读取一个变量属于线程安全。

1.4.2懒汉模式——线程不安全

原因:懒汉模式中的实例化对象是什么时候需要用就什么时候调用getIstance,多个线程同时去调用getIstance的时候,getIstance方法中的赋值是一个修改操作,此时就会发生多个线程对同一个变量进行修改操作就会引起线程不安全。
解决方法:加锁

class SinlgetonLazy1 {public static volatile int count = 0;private static SinlgetonLazy1 instance = null;public static SinlgetonLazy1 getInstance() {Object locker = new Object();synchronized(locker) {if (instance == null ) {instance = new SinlgetonLazy1();}}return instance;}private SinlgetonLazy1() {}
}
public class Deom24 {public static void main(String[] args) throws InterruptedException {SinlgetonLazy1 s = SinlgetonLazy1.getInstance();Thread t1 = new Thread(() -> {SinlgetonLazy1 s1 = SinlgetonLazy1.getInstance();});Thread t2 = new Thread(() -> {SinlgetonLazy1 s2 = SinlgetonLazy1.getInstance();});t1.start();t2.start();}
}

在上述代码中有一个缺陷的地方就是给加锁的时候其实只有在线程第一次调用的时候需要加锁,第二次就是读操作不是修改操作了,毕竟频繁加锁也是一个耗资源的操作,所以我们可以加一个判断来解决这个缺陷。

class SinlgetonLazy1 {public static volatile int count = 0;private static SinlgetonLazy1 instance = null;public static SinlgetonLazy1 getInstance() {Object locker = new Object();if(instance == null) {synchronized(locker) {if (instance == null ) {instance = new SinlgetonLazy1();}}}return instance;}private SinlgetonLazy1() {}
}
public class Deom24 {public static void main(String[] args) throws InterruptedException {SinlgetonLazy1 s = SinlgetonLazy1.getInstance();Thread t1 = new Thread(() -> {SinlgetonLazy1 s1 = SinlgetonLazy1.getInstance();});Thread t2 = new Thread(() -> {SinlgetonLazy1 s2 = SinlgetonLazy1.getInstance();});t1.start();t2.start();}
}

此处有两个if而且内容是一样的,但是表达的意义是不一样的,第一个if是判断线程是否需要加锁,第二个if是指是否创造对象。

2.2.阻塞队列

2.1概念

其实就是一个带有阻塞效果先进先出的队列,队列在生产者消费者模型充当一个媒介的身份。

2.2两种队列

2.2.1阻塞队列

队空的时候会阻塞和队满的时候会阻塞的先进先出的队列,在一个进程中直接使用阻塞队列实现生产者消费者模型

2.2.2消息队列

是一个带有标识的先进先出的队列,当让某一个类型的元素出的时候,其他类型的元素会阻塞,在分布式系统中使用单独部署的消息队列服务器实现生产者消费者模型。

2.3模拟实现阻塞队列

public class MyBlockingQueue {public static int count;public int rear;public int front;public int size = 0;public int[] array;public MyBlockingQueue(int Capacity) {this.array = new int[Capacity];}public void put(int value) throws InterruptedException {synchronized (this) {while(size >= array.length) {this.wait();}array[rear] = value;rear = (rear+1) % array.length;size++;//唤醒take线程的阻塞this.notify();}}public int take() throws InterruptedException {synchronized(this) {while(size == 0) {this.wait();}int ret = array[front];front = (front+1) % array.length;size--;//唤醒put线程的阻塞this.notify();return ret;}}//生产者消费者模型public static void main(String[] args) {MyBlockingQueue myBlockingQueue = new MyBlockingQueue(100);//消费者1Thread t1 = new Thread(() -> {try {while (true) {Thread.sleep(1000);myBlockingQueue.take();System.out.println("消费者消费了一个:"+ count);}} catch (InterruptedException e) {throw new RuntimeException(e);}});//消费者2Thread t2 = new Thread(() -> {try {while (true) {Thread.sleep(1000);myBlockingQueue.take();System.out.println("消费者消费了一个:"+ count);}} catch (InterruptedException e) {throw new RuntimeException(e);}});//生产者Thread t3 = new Thread(() -> {try {while(true) {myBlockingQueue.put(count);System.out.println("生产者生产一个:"+ count);count++;}} catch (InterruptedException e) {throw new RuntimeException(e);}});//t1.start();t2.start();t3.start();}
}

在这里插入图片描述
上述图片中,为什么选择while不选择if,原因在于:
当其他线程中有interrupt打断wait方法,则wait方法就会停止阻塞状态,继续往下执行throws操作,那么就会继续添加元素,那么就有可能会覆盖之前的元素,出现bug。而用while就会避免者种情况发生,while就会再一次判断是否满足条件从而就不会发生if出现的情况。
总结:在使用wait的时候最好搭配while不要搭配if。

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

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

相关文章

leetcode每日一练:顺序表OJ题

第一题:移除元素 题目要求:给一个数组nums和一个值val,你需要 原地 移除所有所有数值等于val的元素,并返回移除后数组的新长度。 不要使用额外的数组空间,你必须仅使用0(1)的额外空间并 原地 修改输入数组。 元素的顺序…

【Tools】AIGC:人工智能生成内容的新时代

那年夏天我和你躲在 这一大片宁静的海 直到后来我们都还在 对这个世界充满期待 今年冬天你已经不在 我的心空出了一块 很高兴遇见你 让我终究明白 回忆比真实精彩 🎵 王心凌《那年夏天宁静的海》 随着人工智能(AI)技术的…

三生随记——午夜咖啡馆

在城市的边缘,隐藏着一间古老的咖啡馆——“午夜咖啡馆”。它的外观不起眼,却总能在夜晚吸引那些寻找安宁或寻求刺激的顾客。据说,咖啡馆的老板是一位年长的绅士,他的脸上总是挂着神秘莫测的微笑。 艾米是一名作家,常常…

基于weixin小程序智慧物业系统的设计

管理员账户功能包括:系统首页,个人中心,管理员管理,用户管理,员工管理,房屋管理,缴费管理,车位管理,报修管理 工作人员账号功能包括:系统首页,维…

使用electron打包Vue前端项目的详细流程

使用electron打包Vue前端项目的详细流程 需要更改的东西 路由模式的修改 # 修改前:url不带#mode: history# 修改后:url带#mode: hash全局修改Cookies为localStorage 由于打包成exe或deb这类可执行文件后,本地是没有 Cookies 全局搜索Cooki…

Android Studio环境搭建(4.03)和报错解决记录

1.本地SDK包导入 安装好IDE以及下好SDK包后,先不要管IDE的引导配置,直接新建一个新工程,进到开发界面。 SDK路径配置:File---->>Other Settings---->>Default Project Structure 拷贝你SDK解压的路径来这,…

ros笔记01--初次体验ros2

ros笔记01--初次体验ros2 介绍安装ros2测试验证ros2说明 介绍 机器人操作系统(ROS)是一组用于构建机器人应用程序的软件库和工具。从驱动程序和最先进的算法到强大的开发者工具,ROS拥有我们下一个机器人项目所需的开源工具。 当前ros已经应用到各类机器人项目开发中…

ORACLE 、达梦 数据库查询指定库指定表的索引信息

在Oracle数据库中,索引是一种关键的性能优化工具,通过它可以加快数据检索速度。在本文中,我们将深入探讨如何详细查询指定表的索引信息,以及如何利用系统视图和SQL查询来获取这些信息。 索引在数据库中的重要性 索引是一种数据结…

操作符详解(下) (C语言)

操作符详解下 操作符的属性1.优先级2.结合级 表达式求值1.整型提升2.如何进行整形提升呢?3.算术转换4.问题表达式解析 操作符的属性 C语言的操作符有2个重要的属性:优先级、结合性,这两个属性决定了表达式求值的计算顺序。 1.优先级 优先级…

问题:第一次世界大战的起止时间是 #其他#学习方法#微信

问题:第一次世界大战的起止时间是 A.1913 ~1918 年 B.1913 ~1918 年 C.1914 ~1918 年 D.1914 ~1919 年 参考答案如图所示

RabbitMQ 进程内流控(Flow Control) 源码解析

1. 概述 1.1 为什么要流控? 流控主要是为了防止生产者生产消息速度过快,超过 Broker 可以处理的速度。这时需要暂时限制生产者的生产速度,让 Broker 的处理能够跟上生产速度。 Erlang进程之间不共享内存,每个进程都有自己的进程邮…

42.HOOK引擎核心代码

上一个内容:41.HOOK引擎设计原理 以 40.设计HOOK引擎的好处 它的代码为基础进行修改 主要做的是读写寄存器 效果图 添加一个类 htdHook.h文件中的实现 #pragma once class htdHook { public:htdHook(); };htdHook.cpp文件中的实现: #include "…

独辟蹊径:我是如何用Java自创一套工作流引擎的(下)

作者:后端小肥肠 创作不易,未经允许严禁转载。 姊妹篇:独辟蹊径:我是如何用Java自创一套工作流引擎的(上)_java工作流引擎-CSDN博客 1. 前言 在上一篇博客中,我们详细介绍了如何利用Java语言从…

LC437.路径总和Ⅲ、LC207.课程表

给定一个二叉树的根节点 root ,和一个整数 targetSum ,求该二叉树里节点值之和等于 targetSum 的 路径 的数目。 路径 不需要从根节点开始,也不需要在叶子节点结束,但是路径方向必须是向下的(只能从父节点到子节点&am…

【每日刷题】Day77

【每日刷题】Day77 🥕个人主页:开敲🍉 🔥所属专栏:每日刷题🍍 🌼文章目录🌼 1. LCR 159. 库存管理 III - 力扣(LeetCode) 2. LCR 075. 数组的相对排序 - 力…

代理ip匿名原理

代理IP匿名的原理基于代理服务器的工作方式。当用户使用代理服务器访问互联网时,请求数据会先发送到代理服务器,然后由代理服务器再向目标服务器发送请求。在这个过程中,代理服务器会将用户真实IP地址替换成代理服务器的IP地址,从…

Linux——passwd文件,grep,cut

/etc/passwd文件含义 作用 - 记录用户账户信息:共分为7段,使用冒号分割 含义 - 文件内容意义:账户名:密码代号x:UID:GID:注释:家目录:SHELL - 第7列/sbin/nologin&#x…

c++(三)

1. STL 1.1. 迭代器 迭代器是访问容器中元素的通用方法。如果使用迭代器,不同的容器,访问元素的方法是相同的;迭代器支持的基本操作:赋值()、解引用(*)、比较(和!&…

隐私计算实训营第二期第11讲组件介绍与自定义开发

首先给大家推荐一个博主的笔记:隐语课程学习笔记11- 组件介绍与自定义开发-CSDN博客 官方文档:隐语开放标准介绍 | 开放标准 v1.0.dev240328 | 隐语 SecretFlow 01 隐语开放标准 隐语开放标准是为隐私保护应用设计的协议栈。 目前,隐语开…

mysql8.0-学习

文章目录 mysql8.0基础知识-学习安装mysql_8.0登录mysql8.0的体系结构与管理体系结构图连接mysqlmysql8.0的 “新姿势” mysql的日常管理用户安全权限练习查看用户的权限回收:revoke角色 mysql的多种连接方式socket显示系统中当前运行的所有线程 tcp/ip客户端工具基于SSL的安全…