单例模式--java代码实现

单例模式

  单例模式,顾名思义,在程序运行中,实例化某个类时只实例化一次,即只有一个实例对象存在。例如在古代,一个国家只能有一个皇帝,在现代则是主席或总统等。

  在Java语言中单例模式有以下实现方式

1.饿汉式

import org.junit.jupiter.api.Test;

public class Singleton {
    //静态成员变量
    private static Singleton singleton = new Singleton();
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
    //构造函数私有化
    private Singleton(){
    }
    //返回静态资源中Singleton实例
    public static Singleton getInstance() {
        return singleton;
    }
    /**
     * 单元测试
     */
    @Test
    public void testSingleton() {
        //通过调用类的静态方法返回类的实例对象
        Singleton s1 = Singleton.getInstance();
        Singleton s2 = Singleton.getInstance();
        s1.setName("zhangsan");
        s2.setName("lisi");
        System.out.println(s1.getName());
        System.out.println(s2.getName());
    }
}

  在类加载时,直接将实例对象初始化,并且该实例是静态的,属于类的成员变量,通过调用类的静态方法返回该对象。  

  运行testSingleton单元测试,输出的两行都是lisi,因为s1,s2指向的同一个实例对象,这个对象在类创建的时候就存在了。

  饿汉式是线程安全的,不管系统的那一个线程获取这个对象,他们都是该类同一个对象。缺点是在程序在一开始就创建了该对象,占用内存空间。下面这种实现方式增加判断,在程序调用时才实例化该对象。

2.懒汉式

import org.junit.jupiter.api.Test;

public class Singleton {
    //不初始化实例对象
    private static  Singleton singleton = null;
    private String name;

    public String getName() {
        return name;
    }
     //构造函数私有化
    private Singleton(){
    }
    public void setName(String name) {
        this.name = name;
    }
    //当被调用时才动态实例化
    public static  Singleton getInstance() {
        if(singleton == null) {    
            singleton = new Singleton();
        }
        return singleton;
    }
}

  懒汉式解决了饿汉式的实例对象初始化占用内存的情况,但是懒汉式存在线程安全的问题,当多个线程同时访问getInstance()方法时,有可能在第一个线程进入if语句是还没new Singleton()时,这时第二个线程判断if的时候就会为真,则会创建新的实例,单例模式设计失败。

  这时需要在getInstance()方法上添加synchronized修饰符,表示线程同步,即当一个线程访问该方法时,其他线程需要等待其释放资源。

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

  这样解决了懒汉式的线程不安全的问题,但是这样会降低系统的性能,当一个线程占用了该资源,其他的线程只能等待,系统的性能大大下降。于是乎第三种实现模式,对懒汉式进行了改进。

3.双重检测

public static  Singleton getInstance() {
        if(singleton == null) {    
            synchronized (Singleton.class) {
                if(singleton==null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

  双重检测,线程访问时有两次检测。

  这里将synchronized放到判断语句里面,这样当第一个线程调用getInstance()方法时,singleton为空,进入synchronized代码块,其他的线程即使也判断singleton为空,则需要等待第一个线程完成资源的使用,这样保证了线程的安全。

  当实例化对象完成时,其他的线程调用getInstance()方法时,直接返回Singleton对象即可,不用再等待,这一点优化了系统,提高了系统的性能。

4.总结

  模式来源于生活。本文用Java语言实现了单例模式。三种实现方式都是线程安全的,饿汉式比较占用内存空间;懒汉式则降低了系统的性能;双重检测是懒汉式的升级,即能保证线程的安全,也能以懒加载的方式实现单例模式,在大规模系统中节省对象的创建时间,提高系统的性能。在系统中共享同一个资源或同一个对象,则可利用单例模式来实现。

  参考B站视频https://www.bilibili.com/video/av34162848

posted @ 2019-04-26 16:48  peter·zhang  阅读(8775)  评论(0编辑  收藏  举报