【概念】
单例模式是一种常用的软件设计模式。在它的核心结构中只包含一个被称为单例的特殊类。通过单例模式可以保证系统中一个类只有一个实例。
单例模式是设计模式中最简单的形式之一。这一模式的目的是使得类的一个对象成为系统中的唯一实例。要实现这一点,可以从客户端对其进行实例化开始。因此需要用一种只允许生成对象类的唯一实例的机制,“阻止”所有想要生成对象的访问。使用工厂方法来限制实例化过程。这个方法应该是静态方法(类方法),因为让类的实例去生成另一个唯一实例毫无意义。
【关键点】
1:是某个类只能有一个实例;
2:是它必须自行创建这个实例;
3:是它必须自行向整个系统提供这个实例。
【分类】
一、懒汉式单例
像一个懒汉一样,什么时候用什么时候才创建对象。代码实现如下:
//懒汉式单例类.在第一次调用的时候实例化自己
public class Singleton {
private Singleton() {}
private static Singleton single=null;
//静态工厂方法
public static Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}
}
但是以上懒汉式单例的实现没有考虑线程安全问题,它是线程不安全的,并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全。
1、在getInstance方法上加同步
- public static synchronized Singleton getInstance() {
- if (single == null) {
- single = new Singleton();
- }
- return single;
- }
2、双重检查锁定
- public static Singleton getInstance() {
- if (singleton == null) {
- synchronized (Singleton.class) {
- if (singleton == null) {
- singleton = new Singleton();
- }
- }
- }
- return singleton;
- }
3、静态内部类
- public class Singleton {
- private static class LazyHolder {
- private static final Singleton INSTANCE = new Singleton();
- }
- private Singleton (){}
- public static final Singleton getInstance() {
- return LazyHolder.INSTANCE;
- }
- }
二:饿汉式单例
像个饿汉一样,类一加载就创建了对象,不管你以后是否用不用这个对象。代码实现如下:
- public class Student {
- // 把构造方法私有,是为了不让外界随意创建对象
- private Student() {
- }
- // 类本身创建一个唯一的对象。
- //加私有是为了不让外界通过类名直接访问s,进而修改s的值
- private static Student s = new Student();//饿汉式,类一加载就会创建这个唯一的对象
- // 提供公共的方法让外界使用
- // 为了让外界能够直接通过类名访问该方法,需要对该方法加静态
- //因为静态方法只能访问静态成员,所以s也要用静态修饰
- public static Student getStudent() {
- // 调用方法只能返回对象的引用
- return s;
- }
- }
public class SingleDemo{
public static void main(String[ ] args){
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1 == s2);
}
}
【饿汉式和懒汉式区别】
从名字上来说,饿汉和懒汉,
饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
另外从以下两点再区分以下这两种方式:
1、线程安全:
饿汉式天生就是线程安全的,可以直接用于多线程而不会出现问题,
懒汉式本身是非线程安全的,为了实现线程安全有几种写法,分别是上面的1、2、3,这三种实现在资源加载和性能方面有些区别。
2、资源加载和性能:
饿汉式在类创建的同时就实例化一个静态对象出来,不管之后会不会使用这个单例,都会占据一定的内存,但是相应的,在第一次调用时速度也会更快,因为其资源已经初始化完成,
而懒汉式顾名思义,会延迟加载,在第一次使用该单例的时候才会实例化对象出来,第一次调用时要做初始化,如果要做的工作比较多,性能上会有些延迟,之后就和饿汉式一样了
在懒汉式中,如果是在多线程环境下,有1号、2号、3号线程同时执行到了if (s == null)这一步,此时1号线程抢到了CPU的执行权,1号线程就通过了if(s==null)这一步,但是当1号执行s = new Student()之前,2号线程又抢到了CPU执行权,因为这时候1号还没有创建s指向的对象,所以2号线程也能通过if(s==null)的验证,来到s = new Student()这一步,从而就会创建两遍s指向的对象。
那么线程安全问题如何解决呢?
可以在可能出现问题的代码上加锁,锁上就行了,代码如下:
<span style="font-size:14px;">public class Student {
private Student() {
}
private static Student s = null;
//在方法之上加锁
public synchronized static Student getStudent() {
if (s == null) {
s = new Student();
}
return s;
}
}
</span>
【小结】
单例模式保证了一个类只能创建一个对象。