@IT·互联网

java设计模式-单例模式及其几种实现方式

2020-08-25  本文已影响0人  methon

1.定义

确保一个类只有一个实例,并提供一个全局访问点。

2.实现方式

2.1 声明即创建对象方式

package com.methon.singleton;

public class SigDemo01 {
    private static SigDemo01 INSTANCE=new SigDemo01 ();
    private SigDemo01 (){};
    public static SigDemo01 getInstance(){
        return INSTANCE;
    }
    public static void main(String[] args) {
        SigDemo01 sig01=SigDemo01 .getInstance();
        SigDemo01 sig02=SigDemo01 .getInstance();
        /* 判断是否是同一个对象 */
        System.out.println(sig01==sig02);
    }
}

其中static声明的静态成员INSTANCE只会在类加载的时候创建一次,new一个对象。
私有的构造函数保证只能在此类中调用,其他类中想要获取实例只能调用getInstance()方法。
返回static对象。保证对象的唯一性。上面的代码也可以写成:

package com.methon.singleton;

public class SigDemo02{
    private static SigDemo02 INSTANCE;
    static {
        INSTANCE=new SigDemo02();
    }
    private SigDemo02(){};
    public static  SigDemo02 getInstance(){
        return INSTANCE;
    }
    public static void main(String[] args) {
        SigDemo02 sig01=SigDemo02.getInstance();
        SigDemo02 sig02=SigDemo02.getInstance();
        /* 判断是否是同一个对象 */
        System.out.println(sig01==sig02);
    }

}

2.2 懒加载方式

以下为懒加载方式的演变,方法逐渐变得趋于完美

2.2.1 错误的懒加载写法(不支持多线程)

package com.methon.singleton;

public class SigDemo03{
    private static SigDemo03 INSTANCE;
    private SigDemo03(){
    }
    public static SigDemo03 getInstance(){
        if(INSTANCE==null){
            INSTANCE=new SigDemo03();
        }
        return INSTANCE;
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(()-> System.out.println(SigDemo03.getInstance().hashCode())).start();
        }
    }

}

如果代码中没有多线程,则程序没有任何问题。但存在多线程的情况下,执行到if(INSTANCE==null)后, INSTANCE=new SigDemo03()语句前,可能有多个线程通过了if(INSTANCE==null)的判断,从而不能保证创建出的对象有且仅有1个。

2.2.2 懒加载的正确写法(加锁)

package com.methon.singleton;

public class SigDemo04{

    private static SigDemo04 INSTANCE;

    private SigDemo04() {
    }

    public static synchronized SigDemo04 getInstance() {
        if (INSTANCE == null) {
            INSTANCE = new SigDemo04();
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(SigDemo04.getInstance().hashCode())).start();
        }
    }
}

对比之前的方法,只是在getInstance()方法上添加了synchronized ,保证了线程安全。但这样做的话降低了程序的执行效率。

2.2.3 为了解决加锁后程序效率变低而采取的错误处理

package com.methon.singleton;

public class SigDemo05{

    private static SigDemo05 INSTANCE;

    private SigDemo05() {
    }

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

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(SigDemo05 .getInstance().hashCode())).start();
        }
    }
}


为了提高效率,在判断INSTANCE == null之后在进行加锁的判断。但是请注意,虽然进行了加锁操作,但是最后线程们都会创建新的对象。

2.2.4 为了解决加锁后程序效率变低而采取的正确处理

package com.methon.singleton;

public class SigDemo06{

    private static SigDemo06 INSTANCE;

    private SigDemo06 () {
    }

    public static SigDemo06 getInstance() {
        if (INSTANCE == null) {
            synchronized (SigDemo06 .class) {
                if (INSTANCE == null) {
                    INSTANCE = new SigDemo06 ();
                }

            }
        }
        return INSTANCE;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(SigDemo06 .getInstance().hashCode())).start();
        }
    }
}


此方式虽然有些繁琐,但是正确的做法。在判断INSTANCE == null后进行加锁,之后在进行判空操作。仍为空后在进行new新的对象。避免了只要为空就会创建新的对象。

2.2.5 较完美的静态内部类方式

package com.methon.singleton;
public class SigDemo07{
    private SigDemo07() {

    }
    public static SigDemo07 getInstance() {
        return SigDemo07Holder.INSTANCE;
    }
    private static class SigDemo07Holder {
        private final static SigDemo07 INSTANCE = new SigDemo07();
    }
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(SigDemo07.getInstance().hashCode())).start();
        }
    }
}

此种方式,避免了声明即创建对象的方式,采用静态内部类的方式,需要时才创建对象。比较完美且代码不冗余。

2.2.6 effictive java给出的方式

package com.methon.singleton;
public enum  SigDemo08{
    INSTANCE;
    public static void main(String[] args) {
        for (int i = 0; i < 100; i++) {
            new Thread(() -> System.out.println(SigDemo08.INSTANCE.hashCode())).start();
        }
    }
}

直接设置成了枚举类型,且仅有一个枚举量INSTANCE。

3.总结

正常需要单例模式的情况一般都需要创建对象。所以是否进行懒加载区别相差不大。故方法2.1满足平常使用的要求。除此之外,方法2.2.4, 2.2.5, 2.2.6更为完美,推荐使用。

上一篇下一篇

猜你喜欢

热点阅读