设计模式之单例模式
定义: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();
}
}
}
此种方式不仅可以解决线程同步问题,还可以防止反序列化,因为枚举类没有构造方法。