定义:Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)
1.饿汉式
public class Singleton { private static final Singleton INSTANCE = new Singleton(); private Singleton(){} public static Singleton getInstance(){ return INSTANCE; } public void method(){ System.out.println("普通方法"); } public static void main(String[] args) { Singleton instance1 = Singleton.getInstance(); Singleton instance2 = Singleton.getInstance(); System.out.println(instance1 == instance2); } }
此种方式在工作中最为简单常用。类加载到内存后,就只被实例化一次,所以只会产生一个实例,JVM保证线程安全。唯一的缺点是不管用到与否,都会在类加载的时候完成实例化。
2.懒汉式
public class SingletonLazy { private static volatile SingletonLazy INSTANCE; private SingletonLazy(){} public static SingletonLazy getInstance(){ if (INSTANCE == null){ synchronized (Singleton.class){ if (INSTANCE == null){ INSTANCE = new SingletonLazy(); } } } return INSTANCE; } public void method(){ System.out.println("普通方法"); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { SingletonLazy instance = SingletonLazy.getInstance(); new Thread(()-> System.out.println(instance.hashCode())).start(); } } }
此种方式虽然达到了按需初始化的目的,但是带来了线程不安全的问题,所以通过加锁的方式解决,但是又带来效率下降的问题,另外变量需要添加volatile关键字,防止指令重排序。
3.静态内部类
public class SingletonInner { private SingletonInner(){} private static class SingletonInnerHolder{ private static final SingletonInner INSTANCE = new SingletonInner(); } public static SingletonInner getInstance(){ return SingletonInnerHolder.INSTANCE; } public void method(){ System.out.println("普通方法"); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { SingletonInner instance = SingletonInner.getInstance(); new Thread(()-> System.out.println(instance.hashCode())).start(); } } }
此种方式解决了上面两种方式的问题,当SingletonInner类被加载的时候,SingletonInnerHolder内部类是不会被加载的,只有在调用getInstance()的时候才会被加载,既达到了懒加载,又保证了只有一个实例。
4.枚举方式
public enum SingletonEnum { INSTANCE; public void method(){ System.out.println("普通方法"); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(()-> System.out.println(SingletonEnum.INSTANCE.hashCode())).start(); } } }
此种方式不仅可以解决线程同步问题,还可以防止反序列化,因为枚举类没有构造方法。