Java设计模式(一)—— 单例模式

Java设计模式(一)—— 单例模式

一、介绍

什么是单例模式?

通俗的讲,就是在应用程序中只需要某个类保留唯一一个实例对象,不希望有更多的实例。单例模式是java设计模式中最简单的设计模式之一,在应用程序中经常被用到。

二、应用场景

单例模式的应用场景有很多,比如线程池、日志对象、缓存、数据库连接池、计算机系统设备管理器等等。这些常常都设计成全局唯一的,方便集中管理,也节省系统的开销。

三、实现方式

实现单例模式要注意以下三点:

1、单例类只能有一个实例,不能从其他对象中new出来, 即构造器用private修饰。
2、单例类必须自己创建自己的唯一实例,需要实现一个方法提供这个实例。
3、单例类必须能给其他对象提供这一实例。

接下来我们讲讲在Java中如何实现单例模式 :


(1)饿汉式

饿汉式,顾名思义指的是在类加载的时候就初始化好对象,不管有没有用到。

public class Singleton {

    private final static Singleton singleton = new Singleton();

    private Singleton(){}

    public static Singleton getInstance(){
        return singleton;
    }

    public void doSomething(){
		// 业务方法	        
    }
}


(2)懒汉式

懒汉式和饿汉式相对,指的在程序加载时不初始化对象,什么时候被引用什么时候才初始化对象,即在第一次使用的时候才去初始化对象,可以避免内存浪费。注意在获取实例的getInstance()方法前加上了synchronized关键字,这是为了保证线程安全,避免多线程同一时刻获取对象时造成生成了多个实例。

public class Singleton {

    private static Singleton singleton = null;

    private Singleton(){}

    public synchronized static Singleton getInstance(){
        if(singleton == null){   // 1
            singleton = new Singleton(); // 2
        }
        return singleton;
    }

    public void doSomething(){
		// 业务方法	        
    }
}


(3)双重检查锁

双重检查锁是在懒汉式基础上演变过来的,当分析懒汉式代码时,你会发现只有在第一次调用获取实例方法时才需要同步。因为仅//2处的代码需要同步,但只有第一次调用才执行此行,后面的其他调用没有执行此行,但都付出了同步的代价。为了提高效率,双重检查锁应运而生,为什么要二次检查,分析双重检查锁代码,当第一次获取实例多个线程并发到达//2处, 第一个线程执行完synchronized的代码块后,后面的线程仍然需要对singleton 进行第二次检查,所以需要对实例对象做两次检查。java内存模型允许无序写入,//4行代码构造器执行之前,变量singleton可能成为非null的,所以singleton需要加上volatile关键字。

public class Singleton {

    private volatile static Singleton singleton = null;

    private Singleton(){}

    public static Singleton getInstance(){
        if(singleton == null){ // 1
            synchronized (Singleton.class){ // 2
                if(singleton == null){ // 3
                    singleton = new Singleton(); // 4
                }
            }
        }
        return singleton;
    }

    public void doSomething(){
		// 业务方法	        
    }
}


(4)静态内部类

这种方式能达到双检锁方式一样的功效,但实现更为简单。这种和饿汉式比较,在类加载时,singleton实例并没有被初始化,需要显示调用getInstance()方法才会转载SingleHolder类,从而初始化singleton实例,所以达到了延时加载的效果。此方法在实际使用中用的最多,推荐此种写法。

public class Singleton {

    private static class SingleHolder{
        private static Singleton singleton = new Singleton();
    }

    private Singleton(){ }

    public static Singleton getInstance(){
        return SingleHolder.singleton;
    }

    public void doSomething(){
		// 业务方法	        
    }

}


(5)枚举

这种方式巧妙的应用了枚举的特点,构造器本身私有,写法简单,自动支持序列化机制,防止多次实例化,获取实例可以通过Singleton.INSTANCE来访问。

public enum Singleton {

    INSTANCE;

    public void doSomething(){
        // 业务方法	        
    }
}

发布于 2019-08-13 10:30