当前位置: 首页 > news >正文

java单例模式

目录

概念

饿汉模式

懒汉模式(单线程)

多线程单例模式的修改

多线程下饿汉模式

多线程下懒汉模式

加锁条件

加锁位置

总结


概念

单例模式是一种设计模式,按照设计模式来写代码,可以使代码不会太差(保证代码的下限),单例模式是指在一个进程中,限制某个类只能有唯一实例,在java中,单例模式有很多写法,我介绍了两种常见写法。

饿汉模式

public class Singletion {private static Singletion instance = new Singletion();public static Singletion getInstance() {return instance;}
}

饿汉模式的核心在于对类进行加载时,对类成员进行初始化,此处由static修饰的instance成员变量就是类成员,可以直接通过get方法拿到实例对象,这样避免了程序员自己去创建实例,保证了只有一个实例对象。

 新建一个主类,来测试是否为唯一实例:

public class dome1 {public static void main(String[] args) {Singletion singletion = Singletion.getInstance();System.out.println(singletion);Singletion singletion1 = Singletion.getInstance();System.out.println(singletion1);}
}

 在IDEA上运行程序,查看结果为:

可以看到通过get方法拿到的对象地址相同,为同一个对象。但是当我们new一个新的对象出来,又违背了只能有一个实例的初衷:

 可以看到new出来的对象是不同的,我们可以通过使用私有的构造方法,使其他类不能实例化对象,达到只能使用get方法来获取对象,在我创建的单例模式中加入私有的构造方法:

public class Singletion {private static Singletion instance = new Singletion();public static Singletion getInstance() {return instance;}private Singletion() {}
}

这是观察到其他类已经无法实例化对象了:

懒汉模式(单线程)

public class SingletonLazy {private static SingletonLazy instance = null;public static SingletonLazy getInstance() {if(instance == null) {instance = new SingletonLazy();}return instance;}private SingletonLazy(){}
}

懒汉模式的核心:在第一次使用时创建实例,提高了效率。在使用get方法时,首先判断instance对象是否为空,为空就创建实例,不为空直接返回。测试是否为同一实例:

 可以看到得到的结果是同一个实例。

多线程单例模式的修改

多线程下饿汉模式

首先,饿汉模式在多线程情况下是安全的,多个线程同时调用get方法,使用的都是同一个实例,不存在示例的创建和修改。创建多个线程,同时使用get方法拿到实例。

public class Dome3 {public static void main(String[] args) {Thread t1 = new Thread(()->{Singletion singletion = Singletion.getInstance();System.out.println(singletion);});Thread t2 = new Thread(()->{Singletion singletion = Singletion.getInstance();System.out.println(singletion);});Thread t3 = new Thread(new Runnable() {@Overridepublic void run() {Singletion singletion = Singletion.getInstance();System.out.println(singletion);}});t1.start();t2.start();t3.start();}
}

观察结果:

 此时可以发现,三个线程拿到的都是同一个实例。

多线程下懒汉模式

在多个线程同时使用懒汉模式的实例时,会出现线程不安全情况,仍然创建多个线程来拿取懒汉模式下创建的唯一单例:

public class Dome4 {public static void main(String[] args) {Thread t1 = new Thread(()->{SingletonLazy singletonLazy = SingletonLazy.getInstance();System.out.println("t1拿到的实例"+singletonLazy);});Thread t2 = new Thread(()->{SingletonLazy singletonLazy = SingletonLazy.getInstance();System.out.println("t2拿到的实例"+singletonLazy);});Thread t3 = new Thread(()->{SingletonLazy singletonLazy = SingletonLazy.getInstance();System.out.println("t3拿到的实例"+singletonLazy);});t1.start();t2.start();t3.start();}
}

观察结果;

此时可以看见t2和t3线程拿到的实例和t1不是相同的实例,在多线程的环境下,上面写的懒汉模式明显没有做到只有唯一实例,下面对单线程情况下的懒汉模式进行分析:

懒汉模式是在程序运行时,先判断是否已经存在唯一实例,没有存在就创建实例,这种判断在多线程中是不安全的。当两个线程同时拿到空实例时,便会创建出两个实例,此时,便打破了单例模式的规则。

这时,加锁就可以解决这个问题:

加锁条件

首先需要判断加锁的条件,试想一下,如果实例已经创建,仍然进行加锁操作,那么就是白白浪费资源,可以先判断一下唯一实例是否被创建,在进行加锁操作。使用if语句判断唯一实例是否为空;

 if(instance == null) {//进行加锁操作}

加锁位置

锁的位置应该是放在出现线程安全问题的地方,是由于if判断和实例化对象(new操作)之间出现线程切换导致线程安全问题,所以应该通过锁把if判断和new操作打包成一个整体来保证线程的安全。

public class SingletonLazy {private static SingletonLazy instance = null;private static Object object = new Object();public static SingletonLazy getInstance() {if(instance == null) {//进行加锁操作synchronized (object) {if(instance == null) {instance = new SingletonLazy();}}}if(instance == null) {instance = new SingletonLazy();}return instance;}private SingletonLazy(){}
}

此时,在此运行之前的测试方法,查看结果:

观察结果,此时三个线程拿到的实例相同,懒汉模式线程不安全的问题也得到了解决。

总结

单例模式的前提是在一个进程中,如果是多个java进程,自然每个进程都可以有一个实例。

单例模式只能避免失误,但不能避免恶意攻击(反射,序列化反序列化)。

关于单例模式的介绍就到这里,单例模式的设计有很多,我的文章中具体展示了两种,并不代表只有两种。 


http://www.mrgr.cn/news/68022.html

相关文章:

  • 8.2 NLP主流任务和快速实践——NLP主流任务和快速实践
  • 快消零售行业企业员工培训的数字化转型
  • C++ 中的 JSON 序列化和反序列化:结构体与枚举类型的处理
  • 详解使用python读写csv,以及将csv数据写入数据库
  • 【Linux驱动开发】通过设备树节点来配置和调用GPIO(pinctrl节点和gpio-controller)
  • oracle-函数-grouping sets(x1,x2,x3...)的妙用
  • 使用LoRA 对千问70B模型进行微调
  • R7:糖尿病预测模型优化探索
  • MinGW-w64_10.0.0 + GCC12_x86_64-12.2.0-release-posix-seh-msvcrt-rt_v10-rev2.zip
  • 怎么快速区分金媒10.3和10.4旗舰版小程序,如果只看数字你就out了表面被忽悠教新手几招!
  • lua入门教程:随机数
  • MOE(Mixture of Experts,混合专家)
  • TESSY学习笔记—project view界面的架构
  • Webserver(4.9)本地套接字的通信
  • adb shell常用命令
  • [复健计划][紫书]Chapter 7 暴力求解法
  • 今日 AI 简报|微软推出通用多智能体系统,支持语音克隆的开源TTS模型,Android 自动化评估等
  • 关于 RK3588多屏显示的时候第二屏幕出现无法矫正的x坐标偏移 的解决方法
  • 哈夫曼编码的实现
  • Android CCodec Codec2 (二十)C2Buffer与Codec2Buffer
  • Linux网络命令:用于查看和修改路由表的重要工具ip route 详解
  • esp32记录一次错误
  • 基于SpringBoot的社区讯息服务小程序【附源码】
  • jdk1.7和jdk1.8有什么区别?
  • 基于Multisim8路抢答器电路仿真电路(含仿真和报告)
  • 关于 Qt+Osg中使用背景图HUD受到后绘制几何图形顶点颜色影响 的解决方法