1.5(设计模式)单例模式

Posted huang-changfan

tags:

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

单例模式提供了创建一个类唯一对象的方式。

单例模式情况下,某一个类只有唯一实例,且该实例可以被其他所有对象引用。

 

单例模式的关键点:

1.构造器私有化

2.实例对象静态化

 

构造器私有化后,无法通过new来创建,只能通过该类提供的方法获取实例对象。

对象静态化后可保证全局有效,使获取的对象始终是一个对象。

 

单例模式的实现方法:

1、懒汉式(线程不安全、延时加载)

延时加载即在调用时才会加载。

//懒汉式,非同步  线程不安全
//由于其非同步,所以严格意义上来说不属于单例模式
//在多线程下无法正常工作。


class Singleton{ 
    private static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
    public void printObject() {
        System.out.println(this);
    }
}

这种单例模式不适用于多线程情况。

 

2、懒汉式(线程安全、延时加载)

//线程安全,可以在多线程中使用,但效率低下。
//
//
class Singleton{   // 懒汉式线程安全
    private static Singleton singleton;
    private Singleton() {}
    public synchronized static Singleton getInstance() {
        if(singleton == null) {
            singleton = new Singleton();
        }
        return singleton;
    }
    
    public void printObject() {
        System.out.println(this);
    }
}

这里通过synchronized关键字对其加锁,保证了在多线程情况下的安全,但是锁住整个方法效率较低。

 

3、饿汉式(线程安全,非延时加载)

即在初始化就完成加载,而不是在调用时完成加载。

//饿汉式
//类在加载的时候就实例化了,就算thread1和thread2同时获取它,
//取到的是类加载时实例化的那个变量的值,所以说是线程安全的。
//饿汉式缺点是类加载是就初始化对象,浪费内存。

class Singleton{
    private static Singleton singleton = new Singleton();
    private Singleton() {}
    public static Singleton getInstance() {
        return singleton;
    }
    public void printObject() {
        System.out.println(this);
    }
    
}

饿汉式在类加载时就创建了对象,避免了多线程访问的并发问题。

但其类加载时就需要实例化对象,消耗内存。

 

4、双重校验锁(线程安全,延时加载)

//双重校验锁
//多线程下能保持高性能
class Singleton{
    private volatile static Singleton singleton;
    private Singleton() {}
    public static Singleton getInstance() {
        if(singleton == null) {
            synchronized(Singleton.class) {  //此时锁住的是类
                if(singleton == null)
                    singleton =  new Singleton();
            }
        }
        return singleton;
    } 
    public void printObject() {
        System.out.println(this);
    }
}

 

采用双检锁既保证了多线程下的安全性,又保持了较好的性能。

因为当对象存在时,第一判空条件上没有添加synchronized,不会降低效率。

后续创建对象实例部分才添加了synchronized关键字锁住了类,然后再判断对象是否为空,保证了安全性。

 

5、登记式/静态内部类(线程安全、延时加载)

class Singleton{
    private Singleton() {};
    //静态内部类
    private static class SingletonHolder{
        private static final Singleton singleton = new Singleton();
    }
    
    public static Singleton getInstance() {
        return SingletonHolder.singleton;
    }
    
    public void printObject() {
        System.out.println(this);
    }
}

静态内部类加载后并不会立刻加载内部的静态属性,只有外部调用内部类才会初始化静态属性,加载实例。

这样既保证了多线程情况下的安全性,而且实现了延迟加载。

 

 

参考资料:

http://www.runoob.com/design-pattern/singleton-pattern.html

 

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

设计模式之单例模式详解(java)

设计模式之单例模式详解(java)

Linux多线程_(线程池,单例模式,读者写者问题,自旋锁)

常用代码片段

常用代码片段

性能比较好的单例写法