Java实现单例的5种方式
转自:https://blog.csdn.net/u014672511/article/details/79774847
1. 什么是单例模式
单例模式指的是在应用整个生命周期内只能存在一个实例。单例模式是一种被广泛使用的设计模式。他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存。
2. 单例模式和静态类的区别
首先理解一下什么是静态类,静态类就是一个类里面都是静态方法和静态field,构造器被private修饰,因此不能被实例化。Math类就是一个静态类。
1)首先单例模式会提供给你一个全局唯一的对象,静态类只是提供给你很多静态方法,这些方法不用创建对象,通过类就可以直接调用;
2)如果是一个非常重的对象,单例模式可以懒加载,静态类就无法做到;
3.如何实现单例模式
class SingletonHungary {private static SingletonHungary singletonHungary = new SingletonHungary();//将构造器设置为private禁止通过new进行实例化private SingletonHungary() {}public static SingletonHungary getInstance() {return singletonHungary;}
}
// 单例模式的懒汉实现1--线程不安全
class SingletonLazy1 {private static SingletonLazy1 singletonLazy;private SingletonLazy1() {}public static SingletonLazy1 getInstance() {if (null == singletonLazy) {try {// 模拟在创建对象之前做一些准备工作Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}singletonLazy = new SingletonLazy1();}return singletonLazy;}
}
public class SingletonLazyTest {public static void main(String[] args) {Thread2[] ThreadArr = new Thread2[10];for (int i = 0; i < ThreadArr.length; i++) {ThreadArr[i] = new Thread2();ThreadArr[i].start();}}}// 测试线程
class Thread2 extends Thread {@Overridepublic void run() {System.out.println(SingletonLazy1.getInstance().hashCode());}
}
124191239
124191239
872096466
1603289047
1698032342
1913667618
371739364
124191239
1723650563
367137303
可以看到他们的hashCode不都是一样的,说明在多线程环境下,产生了多个对象,不符合单例模式的要求。
那么如何使线程安全呢?第二种方法,我们使用synchronized关键字对getInstance方法进行同步。
// 单例模式的懒汉实现2--线程安全
// 通过设置同步方法,效率太低,整个方法被加锁
class SingletonLazy2 {private static SingletonLazy2 singletonLazy;private SingletonLazy2() {}public static synchronized SingletonLazy2 getInstance() {try {if (null == singletonLazy) {// 模拟在创建对象之前做一些准备工作Thread.sleep(1000);singletonLazy = new SingletonLazy2();}} catch (InterruptedException e) {e.printStackTrace();}return singletonLazy;}
}
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
可以看到,这种方式达到了线程安全。但是缺点就是效率太低,是同步运行的,下个线程想要取得对象,就必须要等上一个线程释放,才可以继续执行。
那我们可以不对方法加锁,而是将里面的代码加锁,也可以实现线程安全。但这种方式和同步方法一样,也是同步运行的,效率也很低。
// 单例模式的懒汉实现3--线程安全
// 通过设置同步代码块,效率也太低,整个代码块被加锁
class SingletonLazy3 {private static SingletonLazy3 singletonLazy;private SingletonLazy3() {}public static SingletonLazy3 getInstance() {try {synchronized (SingletonLazy3.class) {if (null == singletonLazy) {// 模拟在创建对象之前做一些准备工作Thread.sleep(1000);singletonLazy = new SingletonLazy3();}}} catch (InterruptedException e) {// TODO: handle exception}return singletonLazy;}
}
我们来继续优化代码,我们只给创建对象的代码进行加锁,但是这样能保证线程安全么?
// 单例模式的懒汉实现4--线程不安全
// 通过设置同步代码块,只同步创建实例的代码
// 但是还是有线程安全问题
class SingletonLazy4 {private static SingletonLazy4 singletonLazy;private SingletonLazy4() {}public static SingletonLazy4 getInstance() {try {if (null == singletonLazy) { //代码1// 模拟在创建对象之前做一些准备工作Thread.sleep(1000);synchronized (SingletonLazy4.class) {singletonLazy = new SingletonLazy4(); //代码2}}} catch (InterruptedException e) {// TODO: handle exception}return singletonLazy;}
}
1210004989
1425839054
1723650563
389001266
1356914048
389001266
1560241484
278778395
124191239
367137303
让我们来继续优化一下,既然上述方式存在问题,那我们在同步代码块里面再一次做一下null判断不就行了,这种方式就是我们的DCL双重检查锁机制。
//单例模式的懒汉实现5--线程安全
//通过设置同步代码块,使用DCL双检查锁机制
//使用双检查锁机制成功的解决了单例模式的懒汉实现的线程不安全问题和效率问题
//DCL 也是大多数多线程结合单例模式使用的解决方案
class SingletonLazy5 {private static volatile SingletonLazy5 singletonLazy;private SingletonLazy5() {}public static SingletonLazy5 getInstance() {try {if (null == singletonLazy) {// 模拟在创建对象之前做一些准备工作Thread.sleep(1000);synchronized (SingletonLazy5.class) {if(null == singletonLazy) {singletonLazy = new SingletonLazy5();}}}} catch (InterruptedException e) {// TODO: handle exception}return singletonLazy;}
}
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
我们可以看到DCL双重检查锁机制很好的解决了懒加载单例模式的效率问题和线程安全问题。这也是我们最常用到的方式。
volatile关键字
这里注意到在定义singletonLazy的时候用到了volatile关键字,这是为了防止指令重排序的,为什么要这么做呢,我们来看一个场景:
3.将singletonLazy对象指向分配的内存空间(注意到这步instance就非null了)。
//使用静态内部类实现单例模式--线程安全
class SingletonStaticInner {private SingletonStaticInner() {}private static class SingletonInner {private static SingletonStaticInner singletonStaticInner = new SingletonStaticInner();}public static SingletonStaticInner getInstance() {try {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}return SingletonInner.singletonStaticInner;}
}
这里提供了静态代码块实现单例模式。这种方式和第一种类似,也是一种饿汉模式。
//使用静态代码块实现单例模式
class SingletonStaticBlock {private static SingletonStaticBlock singletonStaticBlock;static {singletonStaticBlock = new SingletonStaticBlock();}public static SingletonStaticBlock getInstance() {return singletonStaticBlock;}
}
LZ为什么要提序列化和反序列化呢?因为单例模式虽然能保证线程安全,但在序列化和反序列化的情况下会出现生成多个对象的情况。运行下面的测试类,
public class SingletonStaticInnerSerializeTest {public static void main(String[] args) {try {SingletonStaticInnerSerialize serialize = SingletonStaticInnerSerialize.getInstance();System.out.println(serialize.hashCode());//序列化FileOutputStream fo = new FileOutputStream("tem");ObjectOutputStream oo = new ObjectOutputStream(fo);oo.writeObject(serialize);oo.close();fo.close();//反序列化FileInputStream fi = new FileInputStream("tem");ObjectInputStream oi = new ObjectInputStream(fi);SingletonStaticInnerSerialize serialize2 = (SingletonStaticInnerSerialize) oi.readObject();oi.close();fi.close();System.out.println(serialize2.hashCode());} catch (Exception e) {e.printStackTrace();}}}//使用匿名内部类实现单例模式,在遇见序列化和反序列化的场景,得到的不是同一个实例
//解决这个问题是在序列化的时候使用readResolve方法,即去掉注释的部分
class SingletonStaticInnerSerialize implements Serializable {/*** 2018年03月28日*/private static final long serialVersionUID = 1L;private static class InnerClass {private static SingletonStaticInnerSerialize singletonStaticInnerSerialize = new SingletonStaticInnerSerialize();}public static SingletonStaticInnerSerialize getInstance() {return InnerClass.singletonStaticInnerSerialize;}// protected Object readResolve() {
// System.out.println("调用了readResolve方法");
// return InnerClass.singletonStaticInnerSerialize;
// }
}
865113938
1078694789
结果表明的确是两个不同的对象实例,违背了单例模式,那么如何解决这个问题呢?解决办法就是在反序列化中使用readResolve()方法,将上面的注释代码去掉,再次运行:
865113938
调用了readResolve方法
865113938
Java实现单例的5种方式相关推荐
- Java实现单例的几种方式
1. 什么是单例模式 单例模式指的是在应用整个生命周期内只能存在一个实例.单例模式是一种被广泛使用的设计模式.他有很多好处,能够避免实例对象的重复创建,减少创建实例的系统开销,节省内存. 2. 单例模 ...
- 单例设计模式介绍||单例设计模式八种方式——1) 饿汉式(静态常量) 2) 饿汉式(静态代码块) 3) 懒汉式(线程不安全) 4) 懒汉式(线程安全,同步方法)
单例模式 单例设计模式介绍 所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态方法). 比如Hibernate的 ...
- 单例设计模式八种方式——5) 懒汉式(线程安全,同步代码块) 6) 双重检查 7) 静态内部类 8) 枚举
懒汉式(线程安全,同步代码块)应用实例 优缺点说明: 1) 这种方式,本意是想对第四种实现方式的改进,因为前面同步方法效率太低, 改为同步产生实例化的的代码块 2) 但是这种同步并不能起到线程同步的作 ...
- swift实现单例的四种方式
转自:http://www.jianshu.com/p/230cc900948c 单例模式 单例模式是设计模式中最简单的一种,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之 ...
- java实现单例的5种方法,快收藏
1.饿汉式 饿汉式就是类加载后就创建好了单例,直接拿就行,但该方法容易产生垃圾 public class Singleton1 {//构造器私有化 防止外部实例private Singleton1() ...
- java 内部类 单例_确保对象的唯一性——单例模式 (四):一种更好的单例实现方法(静态内部类)...
3.5 一种更好的单例实现方法 饿汉式单例类不能实现延迟加载,不管将来用不用始终占据内存:懒汉式单例类线程安全控制烦琐,而且性能受影响.可见,无论是饿汉式单例还是懒汉式单例都存在这样那样的问题,有没有 ...
- java单例的几种实现方法
java单例的几种实现方法: 方式1: public class Something { private Something() {}private static class LazyHolder { ...
- IOS单例的两种实现方式
单例模式算是开发中比较常见的一种模式了.在iOS中,单例有两种实现方式(至少我目前只发现两种). 根据线程安全的实现来区分,一种是使用@synchronized ,另一种是使用GCD的dispatch ...
- filter java 是单例的吗_JAVA 设计模式之 单例模式详解
单例模式:(Singleton Pattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点.单例模式是创建型模式.单例模式在现实生活中应用也非常广泛. 在 J2EE 标准中,S ...
最新文章
- HashMap根据value值排序
- Python 2退出历史舞台 一句话证明它的重要性
- java类加载-ClassLoader双亲委派机制
- model存储 swift_Swift语言IOS8开发战记10.Data Model
- python两次调用write连续写入的数据之间_两次调用文件的write 方法,以下选项中描述正确的是...
- 【专栏精选】Assetbundle入门
- 读“我为什么不要应届毕业生”
- 古诗词干货整理: 春、夏、秋、冬、山、水、日、月、风、花、雨、雪(简直太全了)
- wallpaper怎么导入视频_vwallpaper2来电视频使用教程
- android 解压jar,解压和打包Jar - 海阔天空玩世不恭的个人空间 - OSCHINA - 中文开源技术交流社区...
- 魔方解法 -- Rubic cube
- Mysql索引、命令重点介绍
- 一文助你入门HTML(❤ ω ❤)
- 电脑内存占用过高怎么办 电脑内存占用过高解决方法
- Handlebars 小技巧
- 根据docker镜像反推dockerfile
- Unity3d UnityEditor编辑器定制和开发插件
- python高级进阶_7_Python动态语言之紧箍咒(slots)03
- UL-1973-2022 储能电池安全标准
- matlab 三叶线,面积计算求三叶线r=asin3φ所围成的面 – 手机爱问
热门文章
- Js获取当前日期时间及其它操作(转)
- Java基础提升高级API
- 利用VS2012自带功能,将xml文档反序列化为对象
- plugin ‘org.springframework.boot:spring-boot-maven-plugin:‘not found
- python抓有趣的东西_Python 五个有趣的彩蛋,你都知道吗?
- 神经科学与“记忆印记”
- 渐进增强和优雅降级有什么区别
- IntellIJ IDEA 配置 Vue 支持
- django处理静态文件
- 炼数成金hadoop视频干货03