java线程安全-单例模式-线程通信

首先看看单例模式的写法

首先我们先来回顾一下饿汉式单例模式:

class Singleton{private static Singleton singleton=new Singleton();private Singleton(){}public static Singleton getInstrance(){return singleton;}
}
public class Test{public static void main(String[] args){Singleton singleton=Singleton.getInstrance();}
}

      饿汉式是在类加载的时候创建实例,故不存在线程安全问题。
虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
      不用我们手工去保证线程安全问题

public class Demo1 {public static void main(String[] args){for(int i=0;i<10;i++) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Singleton.getInstrance());}}).start();}}}
class Singleton{private static Singleton singleton=new Singleton();private Singleton(){System.out.println("我是饿汉模式");}public static Singleton getInstrance(){return singleton;}
}
---------------------------------------------------------------------------------
我是饿汉模式,创建的10个对象是同一个对象
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f
cn.xiong.demo.sychnoized.singleton.Singleton@23b1870f

优点:
类加载时完成初始化,获取对象的速度较快.
缺点:
类加载较慢.

懒汉式单例模式可以这样写:

class Singleton{private static Singleton singleton = null;private Singleton(){}public static Singleton getInstrance(){if(singleton==null){singleton=new Singleton;}return singleton;}
}
-----------------------------------------------------------------------
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@2e3a9cb1
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e
cn.xiong.demo.sychnoized.singleton.SingleTon2@56f2a97e  //下面有一个不同于上面的对象
cn.xiong.demo.sychnoized.singleton.SingleTon2@7b69b864

      现在我们学习多线程相关知识,就很容易能够发现上述懒汉式单例模式为什么线程不安全了。假设开始线程0进入,判断singleton为空,在将要创建实例时,cpu切换,
线程1又进来了,同样singleton为空 创建了实例,这是cpu切换回来到0线程,继续创建实例
可见,经过分析共创建了 两个实例,还谈什么单例。

怎么才能使懒汉式单例模式线程安全呢?

1.synchronized,volatile双层锁处理

public class Demo2 {public static void main(String[] args) {for(int i=0;i<10;i++) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(SingleTon2.getInstance());}}).start();}}}class SingleTon2{private volatile static SingleTon2 singleton=null;    //第二层锁,volatile关键字让内存是否可见有关private SingleTon2() {}public static SingleTon2 getInstance() {if(singleton == null) {    //第一层检查,检查是否有引用指向对象,高并发情况下会有多个线程同时进入synchronized (SingleTon2.class) {  //第一层锁,保证只有一个线程进入,其它线程等执行完毕if(singleton ==null)    //第二层检查,那么第一个线程创建完对象释放锁后,volatile保证了singleton 可见,第二个线程就进不去了singleton = new SingleTon2();}}return singleton;}
}

2.静态内部类

public class StaticInnerClassChuli {public static void main(String[] args) {for(int i=0;i<10;i++) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Singleton3.getInstance());}}).start();}}}class Singleton3{private Singleton3() {}//静态内部类private static class SingleTonHoler{private static Singleton3 singleton3 = new Singleton3();}public static Singleton3 getInstance() {return SingleTonHoler.singleton3;}}

静态内部类的优点是:外部类加载时并不需要立即加载内部类,内部类不被加载则不去初始化INSTANCE,故而不占内存。
      只有当getInstance()方法第一次被调用时,才会去初始化INSTANCE,第一次调用getInstance()方法会导致虚拟机加载SingleTonHoler类,这种方法不仅能确保线程安全,也能保证单例的唯一性,同时也延迟了单例的实例化。

      同上面说的饿汉模式创建静态对象一样,类加载时, 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。
      那么,是不是可以说静态内部类单例就是最完美的单例模式了呢?其实不然,静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,故外部无法传递参数进去,所以,我们创建单例时,可以在静态内部类与懒汉式模式里自己斟酌。
3.枚举类的形式

public class EnumSingleChuli {public static void main(String[] args) {for(int i=0;i<10;i++) {new Thread(new Runnable() {@Overridepublic void run() {System.out.println(Singleton4.INSTANCETON);System.out.println(Singleton4.getInstance());}}).start();}}}enum Singleton4{INSTANCETON;public static Singleton4 getInstance() {return Singleton4.INSTANCETON;}
}

枚举单例模式在《Effective Java》中推荐的单例模式之一。但本人还是喜欢在饿汉模式或静态内部类方法中选择来用

线程通信

      在前面我们讲了很多关于同步的问题,然而在现实开发中,是需要线程之间的协作,也就是需要线程之间实现通信的。

先了解一下最经典的程序设计模式之一的生产者-消费者模型
      日常生活中,每当我们缺少某些生活用品时,我们都会去超市进行购买,那么,你有没有想过,你是以什么身份去的超市呢?相信大部分人都会说自己是消费者,确实如此,那么既然我们是消费者,又是谁替我们生产各种各样的商品呢?当然是超市的各大供货商,自然而然地也就成了我们的生产者。
      如此一来,生产者有了,消费者也有了,那么将二者联系起来的超市又该作何理解呢?
      诚然,它本身是作为一座交易场所而诞生。

      将上述场景类比到我们实际的软件开发过程中,经常会见到这样一幕:
代码的某个模块负责生产数据(供货商),
      而生产出来的数据却不得不交给另一 模块(消费者) 来对其进行处理,
在两个程序模块之间我们必须要有一个类似上述超市的东西来存储数据(超市),这就抽象出了我们的 生产者/消费者模型

其中,产生数据的模块,就形象地称为生产者;
而处理数据的模块,就称为消费者;
生产者和消费者之间的中介就叫做缓冲区,一般就是一个队列。

在生产者-消费者模型中,当队列满时,生产者需要等待队列有空间才能继续往里面放入商品,而在等待的期间内,生产者必须释放对队列的占用权。因为生产者如果不释放对队列的占用权,那么消费者就无法消费队列中的商品,就不会让队列有空间,那么生产者就会一直无限等待下去。
      因此,一般情况下,当队列满时,会让生产者交出对队列的占用权,并进入挂起状态。然后等待消费者消费了商品,然后消费者通知生产者队列有空间了。
      同样地,当队列空时,消费者也必须等待,等待生产者通知它队列中有商品了。

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

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

相关文章

大数据技术之SPARK

Spark Core 什么是 RDD 代码中是一个抽象类&#xff0c;它代表一个弹性的、不可变、可分区、里面的元素可并行计算的集合 弹性 存储的弹性&#xff1a;内存与磁盘的自动切换&#xff1b; 容错的弹性&#xff1a;数据丢失可以自动恢复&#xff1b; 计算的弹性&#xff1a;…

Go 语言范围 (Range)

Go 语言范围 (Range) Go 语言是一种静态强类型、编译型、并发型编程语言&#xff0c;由 Google 开发。它的简洁性和高效性使其成为众多开发者的首选。在 Go 语言中&#xff0c;range 是一个非常有用的关键字&#xff0c;用于遍历数组、切片、字符串以及通道&#xff08;channe…

VUE中数据绑定之OptionAPI

<template> <div> 姓名:<input v-model="userName" /> {{ userName }} <br /> 薪水:<input type="number" v-model="salary" /> <br /> <button v-on:click="submit">提交</button>…

react动态路由

框架的权限控制&#xff08;在config.ts中配置&#xff09; export default {access: {}, }; 权限配置文件&#xff08;access.ts&#xff09; 新建 src/access.ts &#xff0c;在该文件中 export default 一个函数&#xff0c;定义用户拥有的权限 该文件需要返回一个 functi…

Android里面如何优化xml布局

在 Android 开发中&#xff0c;以下是系统化的优化方案&#xff0c;从基础到高级分层解析&#xff1a; 一、基础优化策略 1. 减少布局层级 问题&#xff1a;每增加一层布局&#xff0c;测量/布局时间增加 1-2ms 解决方案&#xff1a; <!-- 避免嵌套 --> <LinearLayo…

基于STM32、HAL库的IP6525S快充协议芯片简介及驱动程序设计

一、简介: IP6525S是一款高性能的同步降压DC-DC转换器芯片,具有以下特点: 输入电压范围:4.5V至32V 输出电压范围:0.8V至30V 最大输出电流:5A 效率高达95% 可编程开关频率(100kHz-1MHz) 支持PWM和PFM模式 内置过流保护、过温保护等功能 该芯片常用于工业控制、通信设备…

二分算法的入门笔记

二分查找 使用前提&#xff1a;有序。可理解为枚举的一种技巧。时间复杂度&#xff1a; l o g ( n ) log(n) log(n) 基础模版代码 使用时根据情景进行相应的变化。注意跳出条件and分支处理方式and返回答案&#xff0c;三者之间的配合&#xff0c;不要进入死循环。可以在模拟…

轻量级Java跨包调用(完全解耦)

Java函数式命令模式 轻量级跨包调用解耦方案&#xff0c;让跨包调用就像调用本地接口那样简单。适用与具有公共依赖的两个jar包&#xff0c;但是又不想相互引入对方作为依赖。 函数式命令模式&#xff0c;很好地实现了跨包调用解耦的目标&#xff0c;并且通过泛型JSON动态转换保…

离散数学问题集--问题5.9

问题 5.9 综合了计算机组成原理、数字逻辑和离散数学中的关键概念&#xff0c;旨在帮助学生理解二进制算术运算的硬件实现、逻辑门与算术运算的关系&#xff0c;以及如何使用数学方法来验证数字系统的正确性。它强调了从规范到实现再到验证的完整过程。 思想 函数抽象&#xf…

OpenLayers:海量图形渲染之矢量切片

最近由于在工作中涉及到了海量图形渲染的问题&#xff0c;因此我开始研究相关的解决方案。在咨询了许多朋友之后发现矢量切片似乎是行业内最常用的一种解决方案&#xff0c;于是我便开始研究它该如何使用。 一、什么是矢量切片 矢量切片按照我的理解就是用栅格切片的方式把矢…

神经网络入门—自定义网络

网络模型 定义一个两层网络 import torch import torch.nn as nn import torch.optim as optim import torch.nn.functional as F# 定义神经网络模型 class Net(nn.Module):def __init__(self, init_x0.0):super().__init__()self.fc1 nn.Linear(1, 10)self.fc2 nn.Linear(…

无人机装调与测试

文章目录 前言一、无人机基本常识/预备知识&#xff08;一&#xff09;无人机飞行原理无人机硬件组成/各组件作用1.飞控2.GPS3.接收机4.电流计5.电调6.电机7.电池8.螺旋桨9.UBEC&#xff08;稳压模块&#xff09; &#xff08;二&#xff09;飞控硬件简介&#xff08;三&#x…

2024年-全国大学生数学建模竞赛(CUMCM)试题速浏、分类及浅析

2024年-全国大学生数学建模竞赛(CUMCM)试题速浏、分类及浅析 全国大学生数学建模竞赛&#xff08;China Undergraduate Mathematical Contest in Modeling&#xff09;是国家教委高教司和中国工业与应用数学学会共同主办的面向全国大学生的群众性科技活动&#xff0c;目的在于激…

Linux入门指南:从零开始探索开源世界

&#x1f680; 前言 大家好&#xff01;今天我们来聊一聊Linux这个神奇的操作系统~ &#x1f916; 很多小伙伴可能觉得Linux是程序员专属&#xff0c;其实它早已渗透到我们生活的各个角落&#xff01;本文将带你了解Linux的诞生故事、发行版选择攻略、应用领域&#xff0c;还有…

记录vscode连接不上wsl子系统下ubuntu18.04问题解决方法

记录vscode连接不上wsl子系统下ubuntu18.04问题解决方法 报错内容尝试第一次解决方法尝试第二次解决方法注意事项参考连接 报错内容 Unable to download server on client side: Error: Request downloadRequest failed unexpectedly without providing any details… Will tr…

Cursor+MCP学习记录

参考视频 Cursor MCP 王炸&#xff01;彻底颠覆我的Cursor工作流&#xff0c;效率直接起飞_哔哩哔哩_bilibili 感觉这个博主讲的还不错 所使用到的网址 Smithery - Model Context Protocol Registry Introduction - Model Context Protocol 学习过程 Smithery - Model …

testflight上架ipa包-只有ipa包的情况下如何修改签名信息为苹果开发者账户对应的信息-ipa苹果包如何手动改签或者第三方工具改签-优雅草卓伊凡

testflight上架ipa包-只有ipa包的情况下如何修改签名信息为苹果开发者账户对应的信息-ipa苹果包如何手动改签或者第三方工具改签-优雅草卓伊凡 直接修改苹果IPA包的签名和打包信息并不是一个推荐的常规做法&#xff0c;因为这可能违反苹果的开发者条款&#xff0c;并且可能导致…

深入解析Java内存与缓存:从原理到实践优化

一、Java内存管理&#xff1a;JVM的核心机制 1. JVM内存模型全景图 ┌───────────────────────────────┐ │ JVM Memory │ ├─────────────┬─────────────────┤ │ Thread │ 共享…

紫光展锐5G SoC T8300:影像升级,「定格」美好世界

影像能力已成为当今衡量智能手机性能的重要标尺之一。随着消费者对手机摄影需求日益提升&#xff0c;手机厂商纷纷在影像硬件和算法上展开激烈竞争&#xff0c;力求为用户带来更加出色的拍摄体验。 紫光展锐专为全球主流用户打造的畅享影音和游戏体验的5G SoC——T8300&#x…

【Java设计模式】第6章 抽象工厂模式讲解

6. 抽象工厂模式 6.1 抽象工厂讲解 定义:提供一个接口创建一系列相关或依赖对象,无需指定具体类。核心概念: 产品等级结构:同一类型的不同产品(如Java视频、Python视频)。产品族:同一工厂生产的多个产品(如Java视频 + Java手记)。适用场景: 需要创建多个相关联的产品…