单例模式是一种创建型模式,它确保程序中一个类最多只有一个实例。

实现单例模式需要注意的几个点:

①需要提供一个静态的获取该类的方法;

②构造方法必须是私有的;

那么都有哪些常见的创建单例的方法呢,这些线程是否安全呢?

第一种:饿汉式 

饿汉式单例是在程序加载的时候就创建了这个对象的实例:
public class HungrySingleton { private static HungrySingleton instancc = new
HungrySingleton();private HungrySingleton(){}; public static HungrySingleton
getInstance(){return instancc; } }
在静态初始化的时候创建单例,JVM在加载这个类的时候就创建此唯一的单实例,JVM保证在任何线程访问instance静态变量之前,一定先创建实例。

但是,当创建一个单例需要消耗大量资源时,而且希望只在程序使用的时候才初始化,那么就需要延迟加载了。

第二种:懒汉式
public class LazySingleton { private LazySingleton(){}; private static
LazySingleton instance =null; public LazySingleton getInstance(){ //在被调用的时候才创建
if (null == instance){ instance = new LazySingleton(); } return instance; } }
但是,这种创建单例的方法在多线程访问的情况下是不安全的,会产生多个实例,下面用代码演示一下多线程访问下的安全性问题:
public class SingletonTest { public static void main(String[] args) { for (int
i = 0; i < 1000; i++) { new Thread() { @Override public void run() {
LazySingleton singleton= LazySingleton.getInstance(); System.out.println(
"当前时间"+System.currentTimeMillis()+"------->"+singleton); } }.start(); } } }
模拟了一下多线程访问这个方法的时候,发现这个实例并不是唯一的,在同一时间出现了两个实例: 当前时间1554299067525------->
chenhuan.designpattern.singleton.LazySingleton@327d19c0 当前时间1554299067525
------->chenhuan.designpattern.singleton.LazySingleton@268d1cb9
当前时间1554299067525------->chenhuan.designpattern.singleton.LazySingleton@268d1cb9

所以,我们需要对这个创建方式加锁:
public class LazySingleton { private LazySingleton(){}; private static
LazySingleton instance =null; public static synchronized LazySingleton
getInstance(){//在被调用的时候才创建 if (null == instance){ instance = new
LazySingleton(); }return instance; } }

加了锁之后,性能会下降很多,我们发现,其实只有在第一次进入的时候才需要加锁,当instance变量被赋值之后,就不需要这个同步了。我们可以利用双重检查加锁来提高它的性能。
public final class SychronizedLazySingleton { private
SychronizedLazySingleton(){ };/** * 声明单例变量 */ private volatile static
SychronizedLazySingleton instance =null; public static SychronizedLazySingleton
getInstance(){//这边加一个判断是为了不让每一步都去拿这个锁,加快效率 if (null == instance){ synchronized
(SychronizedLazySingleton.class){ if (null == instance){ instance = new
SychronizedLazySingleton(); } } }return instance; } }
 volatile关键字是为了防止指令的重排序,在多线程下保证变量的可见性。

第三种:静态内部类的方式
public class HolderSingleton { private HolderSingleton(){}; public static class
Singleton{private static HolderSingleton instance = new HolderSingleton(); } //
调用 Singleton.instance 的时候,才会对单例进行初始化。  public static HolderSingleton
getInstance(){return Singleton.instance; } }
由于在调用 SingletonHolder.instance
的时候,才会对单例进行初始化,而且通过反射,是不能从外部类获取内部类的属性的。所以这种形式,很好的避免了反射入侵。

 

第四种:枚举
public enum SingletonEnum { INSTANCE; public void getInstance(){ }; }
利用javap我们反编译枚举类,发现枚举类会被转换成形如public final class T extends Enum
的定义,而且,枚举中的各个枚举项项是通过static来定义的,

当一个Java类第一次被真正使用到的时候静态资源被初始化、Java类的加载和初始化过程都是线程安全的。

 

友情链接
ioDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:ixiaoyang8@qq.com
QQ群:637538335
关注微信