单例模式

Posted Jemb

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了单例模式相关的知识,希望对你有一定的参考价值。

什么单例模式?

 单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

 

为什么要使用单例模式?

在应用系统开发中,我们常常有以下需求:

- 在多个线程之间,比如servlet环境,共享同一个资源或者操作同一个对象

- 在整个程序空间使用全局变量,共享资源

- 大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。

因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。

 

怎么实现模式?

1.饿汉式。

 1 public class Person {
 2     //用static修饰因为方法是静态的需要调用,final是因为变量只会被赋值一次,保证单例
 3     public static final Person person = new Person();
 4     private String name;
 5     
 6     public String getName() {
 7         return name;
 8     }
 9 
10     public void setName(String name) {
11         this.name = name;
12     }
13     
14     //构造函数私有化保证不能实例化对象
15     private Person() {
16     }
17     
18     //提供一个全局的静态方法
19     public static Person getPerson() {
20         return person;
21     }
22 }

2.懒汉式。

---多线程环境下不能保证单例唯一

 1 public class Person2 {
 2     private String name;
 3     private static Person2 person;
 4     
 5     public String getName() {
 6         return name;
 7     }
 8 
 9     public void setName(String name) {
10         this.name = name;
11     }
12     
13     //构造函数私有化
14     private Person2() {
15     }
16     
17     //提供一个全局的静态方法
18     public static Person2 getPerson() {
19         if(person == null) {    //*如果两个线程都在此处进入,就会创建两个对象,保证不了单例
20             person = new Person2();
21         }
22         return person;
23     }
24 }

---使用同步方法,保证单例唯一,但是锁住了整个方法,而我们只需要锁住“new 对象()”这段,所以会影响到性能

 1 public class Person3 {
 2     private String name;
 3     private static Person3 person;
 4     
 5     public String getName() {
 6         return name;
 7     }
 8 
 9     public void setName(String name) {
10         this.name = name;
11     }
12     
13     //构造函数私有化
14     private Person3() {
15     }
16     
17     //提供一个全局的静态方法,使用同步方法
18     public static synchronized Person3 getPerson() {
19         if(person == null) {
20             person = new Person3();
21         }
22         return person;
23     }
24 }

 

3.双重检查。

为什么要双重检查?

当两个线程都一起通过第一个“person=null”的时候,线程1执行同步代码块的代码,创建对象,    但是因为线程2也已经通过第一个“person=null”判断条件,如果没有再加一层判断,也会创建另一个对象,造成非单例。这时需要在同步代码块加一层判断,线程1执行完对象的创建,这是person就不为空了,以后的线程通不过第二个if条件,自然也就保证了单例。

public class Person4 {
    private String name;
    private static Person4 person;
    
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    
    //构造函数私有化
    private Person4() {
    }
    
    //提供一个全局的静态方法
    public static Person4 getPerson() {
        if(person == null) {
            synchronized (Person4.class) {
                if(person == null) {
                    person = new Person4();
                }
            }
            
        }
        return person;
    }
}

 

主函数:

public class MainClass {
    public static void main(String[] args) {
        //饿汉模式
        Person per1 = Person.getPerson();
        Person per2 = Person.getPerson();
        per1.setName("rr");
        per2.setName("meimei");
    
        System.out.println(per1.getName());
        System.out.println(per2.getName());
        
        System.out.println("-------------------");
        //懒汉模式1
        Person2 per3 = Person2.getPerson();
        Person2 per4 = Person2.getPerson();
        per4.setName("rr");
        per4.setName("meimei");
    
        System.out.println(per3.getName());
        System.out.println(per4.getName());
        
        System.out.println("-------------------");
        //懒汉模式2
        Person3 per5 = Person3.getPerson();
        Person3 per6 = Person3.getPerson();
        per5.setName("rr");
        per6.setName("meimei");
    
        System.out.println(per5.getName());
        System.out.println(per6.getName());
        
        System.out.println("-------------------");
        //双重判断
        Person4 per7 = Person4.getPerson();
        Person4 per8 = Person4.getPerson();
        per7.setName("rr");
        per8.setName("meimei");
    
        System.out.println(per7.getName());
        System.out.println(per8.getName());
        
        System.out.println("-------------------");
        
    }
}

 

以上是关于单例模式的主要内容,如果未能解决你的问题,请参考以下文章

常用代码片段

性能比较好的单例写法

片段作为 Android 中的单例

单例片段或保存网页视图状态

你熟悉的设计模式都有哪些?写出单例模式的实现代码

单例模式以及静态代码块