在有些系统中,为了节省内存资源、保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。
单例(Singleton)模式的定义:指一个类只有一个实例,且该类能自行创建这个实例的一种模式。
单例模式有 3 个特点:
单例模式的优点:
单例模式的缺点:
单例模式看起来非常简单,实现起来也非常简单。单例模式在面试中是一个高频面试题。希望大家能够认真学习,掌握单例模式,提升核心竞争力,给面试加分,顺利拿到 Offer。
单例设计模式分为:饿汉式 、 懒汉式
饿汉式的实现方式又分为 :静态代码块方式 和 静态变量方式
静态变量方式
/** * 单例模式 * 懒汉式 * 静态变量方式 */ public class Singleton1 { private Singleton1(){} private static Singleton1 instance = new Singleton1(); public static Singleton1 getInstance(){ return instance; } }
静态代码块方式
/** * 单例模式 * 懒汉式 * 静态代码块 */ public class Singleton2 { private Singleton2(){} private static Singleton2 instance; static { instance= new Singleton2(); } public static Singleton2 getInstance(){ return instance; } }
单例模式下一个类的实例只允许存在一个
下面可以简单验证一下
public static void main(String[] args) { System.out.println("饿汉式 静态变量方式:"+(Singleton1.getInstance() == Singleton1.getInstance())); System.out.println("饿汉式 静态代码块方式:"+(Singleton2.getInstance() == Singleton2.getInstance())); }
都为true 说明获取的都是一个实例对象
/** * 单例模式 * 懒汉式 */ public class Singleton { private Singleton(){} private static Singleton instance; /*这里必须要加一个同步锁 如果是多线程 线程1进入if 然后线程2拿到了cpu执行权 线程1只能等 这样线程1和线程2都进入了if 会导致创建两个实例 * */ public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
以上代码还是有问题的,除了第一次赋值,之后都是读操作,而读操作是线程安全的不需要加锁,但是偏偏还加了锁,就会导致资源浪费。
所以可以使用双重检查模式,并且加上 volatile 关键字
package singleton.demo2; /** * 单例模式 * 懒汉式 */ public class Singleton { private Singleton(){} private static volatile Singleton instance; public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } }
注意:如果编写的是多线程程序,则不要删除上例代码中的关键字 synchronized,否则将存在线程非安全的问题。如果不删除这关键字就能保证线程安全,但是每次访问时都要同步,会影响性能,且消耗更多的资源,这是懒汉式单例的缺点。
双重检查的方式显得比较繁琐 ,还能使用静态内部类实现懒加载。
JVM在加载外部类的时候不会加载静态内部类的,只有当静态内部类的属性或者方法被访问的时候才会被加载。
package singleton.demo2; public class Singleton2 { private Singleton2(){} private static class singleton{ private static final Singleton2 instance = new Singleton2(); } public static Singleton2 getInstance() { return singleton.instance; }
说明:
第一次加载singlcton类时不会去初始化INSTAMCE,只有第一次调用getInstanse,虚拟机加载SinglstonHlalda工并初始化INSTANCE,这样不仅能确保线程安全,也能保证singleton类的唯一性。
小结:
静态内部类单例模式是一种优秀的单例模式,是开源项目中比较常用的一种单例模式。在没有加任何锁的情况下,保证了多线程下的安全,并且没有任何性能影响和空间的浪费。