单例模式

1 简介


单例模式是一种对象创建型模式。单例模式保证一个类只有一个实例存在,并且同时提供一个全局的方法进行进行访问该实例。

应用场景

  • 在多个线程之间共享或对同一个对象进行操作时

  • 用作全局变量时

  • 大规模系统中为了提高性能,减少对象的创建,节省创建时间

2 几种实现方式


2.1 饿汉式

所谓饿汉式,就是不管三七二十一,一上来就开干。不管你需不需要创建某个对象,只要在加载类的时候就创建。

单例模式_第1张图片
我要吃吃吃吃

** - 实现方式**

实体类:Person.class

public class Person {
    private String name;
    private Integer age;
    
    private static Person person = new Person();

    private Person(){
        
    }
    public static Person getPerson(){
        return person;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    
}

MainClass.class

public class MainClass {
    public static void main(String[] args) {
        Person person = Person.getPerson();
        person.setAge(20);
        person.setName("HAH");
        System.out.println(person.getName());
    }
}

在类加载时就实例化对象,这样可以保证该对象的唯一性,是线程安全的,但是相对的效率可能会降低,因为用户也许就不需要实例化该对象。

2.2 懒汉式

所谓懒汉式,顾名思义,就是用到的时候在创建。也就是说,在类加载的时候不会实例化对象,只有当用户真正的要创建对象的时候再调用方法实例化对象。


单例模式_第2张图片
哪位湿兄可以帮我盖下被子QAQ

- 实现方式

Peison.class

public class Person {
    private String name;
    private Integer age;
    
    private static Person person = null;
    private Person(){
        
    }

    public static Person getPerson(){
        if(person == null){
            person = new Person();
        }
        return person;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }   
}

可见,使用懒汉式可以提高程序的灵活性。但是这样也存在一个很大的缺陷。当第一次调用getPerson( )方法时有多个线程同时访问时,那么就会出现线程同步问题。所以的我们需要在方法上加上synchronized防止多个线程同时访问。

** - 改进**

Person.class

public class Person {
    private String name;
    private Integer age;
    
    private static Person person = null;
    private Person(){
        
    }

    public static synchronized Person getPerson(){
        if(person == null){
            person = new Person();
        }
        return person;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    
}

在给getPerson( )方法上加上synchronized可以避免多个线程第一次同时访问实例化多个对象,但是这样在方法上加上synchronized会大大的降低程序的效率,因为只有第一次调用getPerson( )方法时才会发生线程问题,而第二次及以后调用均不会发生线程问题,这样当有一个线程调用getPerson( )方法时,其它线程都将被阻塞,直到调用方法的线程调用完毕。

2.3 双重检查

双重检查既是在懒汉式的改进基础上进行改进,对实例化对象的代码加锁并加以判断,从而实现线程安全并且和懒汉式相比效率也大大提高。

- 具体实现

Person.class

public class Person {
    private String name;
    private Integer age;
    
    private static Person person = null;
    private Person(){
        
    }
    public static  Person getPerson(){
        if(person == null){                               (1

            synchronized(Person.class){      (2

                if(person == null){
                    person = new Person();
                }
            }
            
        }
        return person;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getAge() {
        return age;
    }
    public void setAge(Integer age) {
        this.age = age;
    }
    
}

3 三种实现方式的比较


  • 饿汉式和双重检查是线程安全的,而未改进的懒汉式不是线程安全的。

  • 改进后的懒汉式效率比较低,因为给方法加上了synchronized,导致了同一时刻只能有一个线程访问该方法,造成其它线程堵塞。

  • 双重检查相对于饿汉式来说灵活性更高,只有用户用到该对象的时候才会实例化,相对于懒汉式效率更高,用户只有第一次访问该,方法才会进入锁,其它线程可能会堵塞,但是以后每次方法都可以并发,线程不会再被阻塞。

最近刚学设计模式,哪理解错了,望各位湿兄大佬多多指正

上一篇:抽象工厂模式
下一篇:原型模式

你可能感兴趣的:(单例模式)