专栏首页做不甩锅的后端JAVA中的单例模式分析(doublecheck和枚举实现)

JAVA中的单例模式分析(doublecheck和枚举实现)

文章目录

在java中,单例模式的实现方法有如下几种:

1.饿汉模式

所为饿汉模式,即一开始就创建一个静态的对象,之后该对象一直存在。这种模式不会有线程安全问题。

package com.dhb.builder.singleton;

public class Singleton1 {
	
	private static Singleton1 instance = new Singleton1();

	private Singleton1() {
	}
	
	public static Singleton1 getInstance() {
		return Singleton1.instance;
	}

}

2.懒汉模式

对于饿汉模式,优点在于实现简单。但是存在一个问题就是 instance 只要 Singleton1被加载就会被创建到static所在的静态方法区。如实现数据库连接池等情况,用这种方法一上来就要创建数据库的连接资源,实际系统中暂不使用。这就造成了资源的浪费。因此,对于这种情况,出现了与之对应的懒汉模式。 即一开始并不创建对象,待需要使用时再new。

package com.dhb.builder.singleton;

public class SingletonDemo1 {
	
	private static SingletonDemo1 instance = null;
	
	private SingletonDemo1() {
		
	}

	/**
	 * 存在线程安全问题
	 * @return
	 */
	public static SingletonDemo1 getInstance() {
		if(instance == null) {
			instance = new SingletonDemo1();
		}
		return instance;
	}
}

这是大家想到的最常用的懒汉模式的写法。但是问题来了,上述模式在多线程的情况下是线程不安全的!也就是说,如果有两个线程,同时getInstance(),同时都会判断instance的值为null。这种情况下会创建多个实例。 为了解决上述问题,我们引入了锁:

package com.dhb.builder.singleton;

public class SingletonDemo2 {

	private static SingletonDemo2 instance = null;

	private SingletonDemo2() {
		
	}

	/**
	 * 增加同步机制,解决线程安全
	 * @return
	 */
	public static synchronized SingletonDemo2 getInstance() {
		if(instance == null) {
			instance = new SingletonDemo2();
		}
		return instance;
	}
}

上面这种做法,确实解决了线程安全问题,但是带来了一个更加不好的问题,那就是每一次请求都会加锁!这样会严重影响性能。更好的做法是采用双重检查机制:

package com.dhb.builder.singleton;

public class SingletonDemo3 {

	private static SingletonDemo3 instance = null;

	private SingletonDemo3() {
		
	}

	/**
	 * 增加双重检查机制,解决synchronized效率问题
	 * @return
	 */
	public static  SingletonDemo3 getInstance() {
		if(instance == null)
			synchronized (SingletonDemo3.class) {
				if (instance == null) {
					instance = new SingletonDemo3();
				}
			}
		return instance;
	}
}

上述单例实际仍然存在问题,那就是类初始化仍然需要时间,如果同时又两个线程同时进入getInstance方法,第一个线程锁定之后,第二个线程判断不为空,则直接使用instalce,如果此时第一个线程对SingletonDemo3对象还没实例化完成,如该对象内部存在一个耗时的引用,如果是一个数据库连接,则会导致第二个线程使用的对象不完整。出现空指针。因此更好的写法是加上volatile。以保证happen-before原则。

package com.dhb.builder.singleton;

public class SingletonDemo4 {

	private volatile static SingletonDemo4 instance = null;

	private SingletonDemo4() {
		
	}

	/**
	 * 增加双重检查机制,解决synchronized效率问题
	 * @return
	 */
	public static SingletonDemo4 getInstance() {
		if(instance == null)
			synchronized (SingletonDemo4.class) {
				if (instance == null) {
					instance = new SingletonDemo4();
				}
			}
		return instance;
	}
}

这样单例模式才完全解决。上述方法比较冗繁,有没有更好的解决办法呢,有幸阅读过《effective java》这本书对于单例有更好的解决办法。

3.更好的解决办法

第一种方法,利用静态内部类:

package com.dhb.builder.singleton;

import java.util.stream.IntStream;

public class SingletonHolder {

    private SingletonHolder() {

    }
    private static class InstanceHolder{
        private final static SingletonHolder INSTANCE = new SingletonHolder();
    }

    public static SingletonHolder getInstance() {
        return InstanceHolder.INSTANCE;
    }

    public static void main(String[] args) {
        IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
            @Override
            public void run() {
                System.out.println(SingletonHolder.getInstance());
            }
        }.start());
    }
}

上述方法执行结果:

com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6
com.dhb.builder.singleton.SingletonHolder@55c1b7a6

可以看出SingletonHolder类只实例化了一次。这种方法很巧妙地利用一个内部类,很简单的代码即实现了单例,而且是线程安全。

方式二:《effective java》中还有一种更简单的写法,那就是枚举。也是《effective java》作者最为推崇的方法。

package com.dhb.builder.singleton;

import java.util.stream.IntStream;

public class SingletonEnum {

	private SingletonEnum() {

	}

	private enum Singleton {
		INSTANCE;

		private final SingletonEnum instance;

		Singleton() {
			instance = new SingletonEnum();
		}

		public SingletonEnum getInstance() {
			return instance;
		}
	}

	public static SingletonEnum getInstance() {
		return Singleton.INSTANCE.getInstance();
	}

	public static void main(String[] args) {
		IntStream.rangeClosed(1,100).forEach(i -> new Thread(String.valueOf(i)){
			@Override
			public void run() {
				System.out.println(SingletonEnum.getInstance());
			}
		}.start());
	}
}

上述方法执行结果:

com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6
com.dhb.builder.singleton.SingletonEnum@55c1b7a6

在java中,枚举天然实现了单例模式。其构造方法只会实例化一次。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 11 Confluent_Kafka权威指南 第十一章:流计算

    kafka 传统上被视为一个强大的消息总线,能够处理事件流,但是不具备对数据的处理和转换能力。kafka可靠的流处理能力,使其成为流处理系统的完美数据源,Apa...

    冬天里的懒猫
  • 聊聊java中的哪些Map:(一)HashMap(1.8)源码分析

    无论是大厂还是不知名的小公司,HashMap都是一个绕不开的话题。基本上,如果通过HashMap能聊半小时以上,基本offer就没什么大碍了。现在我们也看...

    冬天里的懒猫
  • The basics of the InnoDB undo logging and history system(13.innoDB undo log 和历史记录的基本知识)

    InnoDB实现了多版本并发控制(MVCC),这意味着不同的用户将看到他们交互的数据的不同版本(有时称为快照,这是一个有点误导人的术语)。这样做是为了允许用户看...

    冬天里的懒猫
  • Python之 ansible 动态In

    Ansible Inventory 是包含静态 Inventory 和动态 Inventory 两部分的,静态 Inventory 指的是在文件中指定的主机和组...

    py3study
  • 疯狂java笔记之常用的内部排序

    在计算机程序开发过程中,经常需要一组数据元素(或记录)按某个关键字进行排序,排序完成的序列可用于快速查找相关记录。

    HelloJack
  • String 的 intern() 方法解析

    JDK7 之前和之后的版本,String 的 intern() 方法在实现上存在差异,本文的说明环境是 JDK8,会在文末说明 intern() 方法的版本差异...

    JMCui
  • python下安装Image包

    py3study
  • 9.1 查找

    1、查找表(Search Table)是由同一类型的数据元素(或记录)构成的集合。

    闫小林
  • modelsim se 2019.2安装教程

    modelsim se 2019是一款在原版本软件功能和性能基础上得到改进以及优化的最新版本HDL语言仿真软件,使其软件功能性更加完善。2019新版本提供全面完...

    碎碎思
  • 安装免费版pymol

    Pymol是一个开源项目,现在由Schrödinger开发、支持和管理,现在已经更新到2.4版。有企业版、政府和学术版、教学版,还有开源版。除了开源版和教学版外...

    用户1359560

扫码关注云+社区

领取腾讯云代金券