iis服务器助手广告广告
返回顶部
首页 > 资讯 > 精选 >java单例模式和线程安全问题怎么解决
  • 928
分享到

java单例模式和线程安全问题怎么解决

2023-07-05 22:07:57 928人浏览 安东尼
摘要

这篇文章主要介绍“java单例模式和线程安全问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java单例模式和线程安全问题怎么解决”文章能帮助大家解决问题。单例模式、多实例模式、和线程安全

这篇文章主要介绍“java单例模式和线程安全问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java单例模式和线程安全问题怎么解决”文章能帮助大家解决问题。

    单例模式、多实例模式、和线程安全

    单例模式

    单例模式是指确保一个类仅有一个唯一的实例,并且提供了一个全局的访问点。

    分类: 懒汉式、饿汉式

    为什么需要单例模式?

    再某些特殊的情况下,存在一个类仅能用来产生一个唯一对象的必要性。例如:打印机室有许多打印机,但是它的打印管理系统只有一个打印任务控制对象,该对象管理打印排队并分配打印任务给各个打印机。单例模式正是为了解决这样的需求而产生的。

    实现思路:

    为了防止客户端利用构造器创建多个对象,将构造方法声明为 private 类型。但这样会使得这个类不可用,所以必须提供一个可以获得实例的静态方法,通常称为 getInstance 方法, 该方法返回一个实例。这个方法必须是静态的,因为静态方法是根据类名调用的,否则也是无法使用的。

    类图:懒汉式

    java单例模式和线程安全问题怎么解决

    类图:饿汉式

    java单例模式和线程安全问题怎么解决

    先来看一个简单的例子:

    测试单例类:Dog’

    //懒汉式public class Dog {private static Dog dog;private String name;private int age;//私有的构造器private Dog() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}//静态工厂方法public static Dog getInstance() {if (dog == null) {dog = new Dog();}return dog;}@Overridepublic String toString() {return "Dog [name=" + name + ", age=" + age + "]";}}

    测试单例类:Cat

    //饿汉式public class Cat {private static Cat cat = new Cat();private String name;private int age;//私有构造器private Cat() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}//静态工厂方法public static Cat getInstance() {return cat;}@Overridepublic String toString() {return "Cat [name=" + name + ", age=" + age + "]";}}

    测试类

    import java.util.HashSet;import java.util.Set;public class Client {public static void main(String[] args) {//单线程模式测试Dog dog1 = Dog.getInstance();Dog dog2 = Dog.getInstance();System.out.println("dog1 == dog2: "+(dog1 == dog2));Cat cat1 = Cat.getInstance();Cat cat2 = Cat.getInstance();System.out.println("cat1 == cat2: "+(cat1 == cat2));}}

    运行结果

    java单例模式和线程安全问题怎么解决

    懒汉式和饿汉式对比

    创建区别

    懒汉式是在第一次调用静态方法 getInstance() 时创建单例对象。
    饿汉式是在类加载时创建单例对象,即在声明静态单例对象时实例化单例类。

    线程安全

    懒汉式是线程不安全的,而饿汉式是线程安全的(下面会测试)。

    资源占用

    懒汉式是等到使用时才会创建,而饿汉式是在类加载时创建。所以懒汉式没有饿汉式快,但是饿汉式比较占用资源,如果一直不使用,会很占据资源。

    多线程模式下的安全性

    多线程类

    import java.util.HashSet;import java.util.Set;public class DogThread extends Thread{private Dog dog;private Set<Dog> set;public DogThread() {set = new HashSet<>();}//这个方法是为了测试添加的。public int getCount() {return set.size();}@Overridepublic void run() {dog = Dog.getInstance();set.add(dog);}}

    多线程测试类

    import java.util.HashSet;import java.util.Set;public class Client {public static void main(String[] args) {//单线程模式测试Dog dog1 = Dog.getInstance();Dog dog2 = Dog.getInstance();System.out.println("dog1 == dog2: "+(dog1 == dog2));Cat cat1 = Cat.getInstance();Cat cat2 = Cat.getInstance();System.out.println("cat1 == cat2: "+(cat1 == cat2));//多线程模式测试DogThread dogThread = new DogThread();Thread thread = null;for (int i = 0; i < 10; i++) {thread = new Thread(dogThread);thread.start();}try {Thread.sleep(2000); //主线程等待子线程完成!} catch (InterruptedException e) {e.printStackTrace();}System.out.println("dog's number: "+dogThread.getCount());}}

    运行结果
    注意:多线程的结果是很难预测的,这里涉及线程的竞争,可能多次运行结果是一样的(多次一样并不代表是绝对正确),但是只要多次测试,就能看到不一样的结果。

    java单例模式和线程安全问题怎么解决

    java单例模式和线程安全问题怎么解决

    说明

    这里我使用一点集合的技巧,利用 Set 集合的特性,把每次产生的 dog 对象存入 Set集合中,最后只要调用集合的 size() 方法就行了。可以看出来产生了两个 dog 对象,这就是产生了错误,这就是属于编程错误了。还要明白多线程下不一定会出错,所以产生的 dog 对象小于线程数。
    由于 饿汉式单例 是线程安全的,这里就不测试了,有兴趣的可以测试一下。

    解决懒汉式单例线程安全的方法:同步
    注意:同步有很多种方法,也可以使用 Lock 进行处理,同步是一种方法,不是特指 synchronzied 这个关键字,感兴趣的人可以多探究一下。
    并且同步的方法通常比较慢,性能方面也要权衡。

    //静态同步工厂方法public synchronized static Dog getInstance() {if (dog == null) {dog = new Dog();}return dog;}

    多实例模式

    这里补充一个多实例的模式,就是对象数量是固定数目的。可以看出单例模式的推广。当然了实现方式也有很多,大家可以尝试以下,这里是我的方式。

    多实例模式类

    //固定数目实例模式public class MultiInstance {//实例数量,这里为四个private final static int INSTANCE_COUNT = 4;private static int COUNT = 0;private static MultiInstance[] instance = new MultiInstance[4];private MultiInstance() {};public static MultiInstance getInstance() {//注意数组的下标只能为 COUNT - 1if (MultiInstance.COUNT <= MultiInstance.INSTANCE_COUNT - 1) {instance[MultiInstance.COUNT] = new MultiInstance();MultiInstance.COUNT++;}//返回实例前,执行了 COUNT++ 操作,所以 应该返回上一个实例return MultiInstance.instance[MultiInstance.COUNT-1];  }}

    测试类

    import java.util.HashSet;import java.util.Set;public class Test {public static void main(String[] args) {System.out.println("------------------------");testMultiInstance();}//测试多实例模式(单例的扩展,固定数目实例)public static void testMultiInstance() {Set<MultiInstance> instanceSet = new HashSet<>();MultiInstance instance = null;for (int i = 0; i < 10; i++) {instance = MultiInstance.getInstance();instanceSet.add(instance);}System.out.println("8个实例中,不同的实例有:"+instanceSet.size());   }}

    运行结果
    注意:如果在多线程环境下使用,也是要考虑线程安全的。感兴趣的可以自己实现一下。

    java单例模式和线程安全问题怎么解决

    单例模式一定是安全的吗?

    不一定,有很多方法可以破坏单例模式!

    这里举例看一看(我只能举我知道的哈!其他的感兴趣,可以去探究一下!)
    使用反射:这种办法是非常有用的,通过反射即使是私有的属性和方法也可以访问了,因此反射破坏了类的封装性,所以使用反射还是要多多小心。但是反射也有许多其他的用途,这是一项非常有趣的技术(我也只是会一点点)。

    使用反射破坏单例模式测试类

    这里使用的还是前面的 Dog 实体类。注意我这里的**包名:**com。
    所有的类都是在 com包 下面的。

    import java.lang.reflect.Constructor;import java.lang.reflect.InvocationTargetException;public class Client {public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, SecurityException, InstantiationException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {Class<?> clazz = Class.forName("com.Dog");Constructor<?> con = clazz.getDeclaredConstructor();//设置可访问权限con.setAccessible(true);Dog dog1 = (Dog) con.newInstance();Dog dog2 = (Dog) con.newInstance();System.out.println(dog1 == dog2);}}

    说明:反射的功能是很强大的,从这里既可以看出来,正是有了反射,才使得Java 语言具有了更多的特色,这也是Java的强大之处。

    使用对象序列化破坏单例模式

    测试实体类:Dog(增加一个对象序列化接口实现)

    import java.io.Serializable;//懒汉式public class Dog implements Serializable{private static final long serialVersionUID = 1L;private static Dog dog;private String name;private int age;//私有的构造器private Dog() {}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}//静态工厂方法public synchronized static Dog getInstance() {if (dog == null) {dog = new Dog();}return dog;}@Overridepublic String toString() {return "Dog [name=" + name + ", age=" + age + "]";}}

    对象序列化测试类

    import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;public class Client {public static void main(String[] args) throws IOException, ClassNotFoundException {Dog dog1 = Dog.getInstance();dog1.setName("小黑");dog1.setAge(2);System.out.println(dog1.toString());ByteArrayOutputStream bos = new ByteArrayOutputStream();ObjectOutputStream oos = new ObjectOutputStream(bos);oos.writeObject(dog1);ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());ObjectInputStream ois = new ObjectInputStream(bis);Dog dog2 = (Dog) ois.readObject();System.out.println(dog2.toString());System.out.println("dog1 == dog2: "+(dog1 == dog2));}}

    运行结果

    java单例模式和线程安全问题怎么解决

    说明
    这里可以看出来通过对象序列化(这里也可以说是对象的深拷贝或深克隆),
    同样也可以实现类的实例的不唯一性。这同样也算是破坏了类的封装性。对象序列化和反序列化的过程中,对象的唯一性变了。

    这里具体的原因很复杂,我最近看了点深拷贝的知识,所以只是知其然不知其之所以然。(所以学习是需要不断进行的!加油诸位。)
    这里我贴一下别的经验吧:(感兴趣的可以实现一下!)

    为什么序列化可以破坏单例了?
    答:序列化会通过反射调用无参数的构造方法创建一个新的对象。

    这个东西目前超出了我的能力范围了,但也是去查看源码得出来的,就是序列化(serializable)和反序列化(externalizable)接口的详细情况了。但是有一点,它也是通过反射来做的的,所以可以看出**反射(reflect)**是一种非常强大和危险的技术了。

    关于“java单例模式和线程安全问题怎么解决”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识,可以关注编程网精选频道,小编每天都会为大家更新不同的知识点。

    --结束END--

    本文标题: java单例模式和线程安全问题怎么解决

    本文链接: https://www.lsjlt.com/news/355796.html(转载时请注明来源链接)

    有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

    本篇文章演示代码以及资料文档资料下载

    下载Word文档到电脑,方便收藏和打印~

    下载Word文档
    猜你喜欢
    • java单例模式和线程安全问题怎么解决
      这篇文章主要介绍“java单例模式和线程安全问题怎么解决”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“java单例模式和线程安全问题怎么解决”文章能帮助大家解决问题。单例模式、多实例模式、和线程安全...
      99+
      2023-07-05
    • Java单例模式中的线程安全问题怎么解决
      今天小编给大家分享一下Java单例模式中的线程安全问题怎么解决的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一. 使用多线程...
      99+
      2023-07-02
    • Java 单例模式线程安全问题
      Java 单例模式线程安全问题SpringIOC容器默认提供bean的访问作用域是单例模式。即在整个application生命周期中,只有一个instance。因此在多线程并发下,会有线程安全风险。我们在MVC框架下的servlet就是线程...
      99+
      2023-05-31
      java 单例模式 线程安全
    • Java单例模式中的线程安全问题
      目录一. 使用多线程需要考虑的因素二. 单例模式1. 饿汉模式2. 懒汉模式3. 懒汉模式(使用synchronized改进)4. 懒汉模式(使用双重校验锁改进)三. volatil...
      99+
      2024-04-02
    • 浅谈单例模式和线程安全问题
      目录单例模式、多实例模式、和线程安全单例模式懒汉式和饿汉式对比多线程模式下的安全性多实例模式总结单例模式、多实例模式、和线程安全 单例模式 单例模式是指确保一个类仅有一个唯一的实例,...
      99+
      2023-05-14
      java单例模式 java线程安全
    • Java单例模式的线程安全,饿汉和懒汉模式详解
      单例模式 创建唯一的一个变量(对象),在类中将构造函数设为protected或者private(析构函数设为相对应的访问权限),故外部不能实例化对象,再提供访问它的一个全局访问点,即...
      99+
      2024-04-02
    • java treemap线程安全问题怎么解决
      要解决Java TreeMap的线程安全问题,有以下几种方法:1. 使用Collections.synchronizedMap()方...
      99+
      2023-10-20
      java
    • Java多线程 - 线程安全和线程同步解决线程安全问题
      文章目录 线程安全问题线程同步方式一: 同步代码块方式二: 同步方法方式三: Lock锁 线程安全问题 线程安全问题指的是: 多个线程同时操作同一个共享资源的时候可能会出现业务安全问题,称为线程安全问题。 举例:...
      99+
      2023-08-20
      java 安全 jvm
    • PHP中单例模式的线程安全性问题思考
      在PHP编程中,单例模式是一种常用的设计模式,它可以确保一个类只有一个实例,并且提供一个全局的访问点来访问这个实例。然而,在多线程环境下使用单例模式时,需要考虑线程安全性的问题。单例模式的最基本实现包括一个私有的构造函数、一个私有的静态变量...
      99+
      2023-10-21
      单例模式 PHP编程 线程安全性
    • Java多线程之线程安全问题怎么解决
      本篇内容主要讲解“Java多线程之线程安全问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java多线程之线程安全问题怎么解决”吧!1.线程安全概述1.1什么是线程安全问题首先我们需要...
      99+
      2023-06-30
    • Java SimpleDateFormat线程不安全问题怎么解决
      本篇内容主要讲解“Java SimpleDateFormat线程不安全问题怎么解决”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“Java SimpleDateFormat线程...
      99+
      2023-07-05
    • 使用springboot单例模式与线程安全问题踩的坑
      springboot单例模式与线程安全问题踩的坑 最近有客户反映,使用公司产品时,偶尔会存在崩溃情况,自己测试无问题,然后去查日志,是报空指针。 于是顺藤摸瓜 往上找,好嘛,之前的开...
      99+
      2024-04-02
    • java单例模式解决了哪些问题
      Java单例模式解决了以下问题: 限制了类的实例化,保证一个类只有一个实例。这样可以节省系统资源,提高性能。 提供了对类实例...
      99+
      2023-10-20
      java
    • Java线程安全问题的解决方案
      目录线程安全问题演示解决线程安全问题1.原子类AtomicInteger2.加锁排队执行2.1 同步锁synchronized2.2 可重入锁ReentrantLock3.线程本地变...
      99+
      2024-04-02
    • C++如何解决单例懒汉式和多线程问题
      这篇文章主要为大家展示了“C++如何解决单例懒汉式和多线程问题”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“C++如何解决单例懒汉式和多线程问题”这篇文章吧。单例懒汉式和多线程问题作为单例模式,...
      99+
      2023-06-04
    • golang map线程安全问题怎么解决
      在Go语言中,Map是并发不安全的,如果多个goroutine同时对同一个Map进行读写操作,可能会导致数据竞争问题。 为了解决Ma...
      99+
      2024-02-29
      golang
    • Java使用线程同步解决线程安全问题详解
      第一种方法:同步代码块: 作用:把出现线程安全的核心代码上锁 原理:每次只能一个线程进入,执行完毕后自行解锁,其他线程才能进来执行 锁对象要求:理论上,锁对象只要对于当前同时执行的线...
      99+
      2024-04-02
    • Java多线程下解决数据安全问题
      目录同步代码块同步方法lock锁同步代码块 基本语句 synchronized (任意对象) { 操作共享代码 } 代码示例 public class SellTicket ...
      99+
      2024-04-02
    • Java多线程之线程安全问题详解
      目录1. 什么是线程安全和线程不安全?2. 自增运算为什么不是线程安全的?3. 临界区资源和竞态条件总结:面试题: 什么是线程安全和线程不安全?自增运算是不是线程安全的?如何保证多线...
      99+
      2024-04-02
    • java中线程安全问题举例分析
      这篇文章主要讲解了“java中线程安全问题举例分析”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java中线程安全问题举例分析”吧!一、什么时候数据在多线程并发的环境下会存在安全问题?三个条...
      99+
      2023-06-21
    软考高级职称资格查询
    编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
    • 官方手机版

    • 微信公众号

    • 商务合作