java设计模式——单例模式

java设计模式——单例模式,第1张

java设计模式——单例模式

单例模式

一、介绍二、单例模式的种类

2.1、饿汉式:

2.1.1、代码实现2.1.2、注意事项 2.2、懒汉式

2.2.1、代码实现 2.3、双重检测模式

2.3.1、代码实现 2.4、静态代码块模式

2.4.1、代码实现 2.5、枚举模式

2.5.1、代码实现

一、介绍

单例模式是指在内存中只会创建且仅创建一次对象的设计模式。应用场景在程序中多次使用同一个对象且作用相同时,为了防止频繁地创建对象使得内存飙升,单例模式可以让程序仅在内存中创建一个对象,让所有需要调用的地方都共享这一单例对象。 二、单例模式的种类 2.1、饿汉式:

饿汉式实现的手法有两种:(1)静态常量,(2)静态代码块;在类加载时已经创建好该单例对象,等待被程序使用 2.1.1、代码实现

静态常量

public class Hungry {
    
     public static final Hungry hungry = new Hungry();
     
    //必须要将构造器用private进行修饰
    private Hungry() {
    }
    
    //通过该方法获取hungry
    private static Hungry getInstance() {
        return hungry;
    }
}

静态代码块

public class Hungry {
 	
    public static final Hungry hungry2;

	//在静态代码块里初始化
    static {
        hungry2 = new Hungry();
    }

    //必须要将构造器用private进行修饰
    private Hungry() {
    }
 
    //通过该方法获取hungry
    private static Hungry getInstance2() {
        return hungry2;
    }
}
2.1.2、注意事项

这种方式只适合在单线程中使用;这种方式可能存在内存浪费; 2.2、懒汉式

在真正需要使用对象时才去创建该单例类对象;懒汉式一共有三种方式,每种方式各有弊端。 2.2.1、代码实现

方式一:通过判断single是否被初始化,从而进行对象的实例化。如果该对象还未被实例化,则会进行实例化。如果已经实例化,则不会再实例化。但是,这种方式存在线程安全问题。假设有一个线程刚刚将if (null == single)条件实行完,时间片就完了。然后另一个线程又来获取该对象,此时,single还未被初始化,所以第二次进来的线程会进行new过程。当第一次的那个线程再次获取到时间片后,会从上一次运行的地方接着运行,所以会再次执行new *** 作。所以,一个实例被new了两次,这就不符合单例模式的定义了。优缺点说明:该模式起到了Lazy Loading的效果,但是只能在单线程下使用。如果在多线程下,一个线程进入了if(single == null)判断语句块,还未来得及往下执行,另一个线程也通过了这个判断语句,这时便会产生多个实例。所以,在多线程环境下不可使用这种方式。结论:在实际开发中,不要使用这种方式。

public class Single {
    private static Single single;

    private Single() {
    }

    //懒汉模式一:存在线程安全问题
    private static Single getInstance() {
        if (null == single) {
            single = new Single();
        }

        return single;
    }
}

方式二:通过给方法加上锁,从而避免了多个线程同时进入该方法中去。因此不会造成单线程安全问题。但是,单例模式只需要new一次,所以,如果这样写,在每次调用该方法时都要进行锁的获取,如果该方法被其他线程占用着,则下一个线程到来后不能及时运行该方法。从而降低效率。优缺点说明:解决了线程不安全问题。效率太低了,每个线程在想获得实例的时候,执行getInstance()方法都要进行同步。而其实这个方法只执行一次实例化代码就行了。后面想获得该实例,直接return就行。方法同步效率太低了。结论:在实际开发中,不推荐使用这种方式。

public class Single {
    private static Single single;

    private Single() {
    }
    
    //懒汉模式二:不存在线程安全模问题。
    //但是每次get都要进行同步,效率太低。毕竟单例只要new一次。
    private static synchronized Single getInstance2() {
        if (null == single) {
            single = new Single();
        }

        return single;
    }
}

方式三:因为方式二解决了多线程安全问题,所以就有人提出,可以通过改变锁的位置,从而解决方式二的效率低下的问题。更改方式如下面代码所示;但是,通过更改代码,虽然解决了效率低下的问题,但是又将线程不安全问题给再次引入。理解方式,和方式二同理。优缺点说明:这种方式本意是对上一个方式的改进,因为上一个方式的效率太低。但是这种同步并不能起到线程同步的作用。结论:在实际开发中,不能使用这种方式。

public class Single {
    private static Single single;

    private Single() {
    }
    
    //懒汉模式三:解决了效率问题,但是有存在线程安全问题
    private static Single getInstance3() {
        if (null == single) {
            synchronized (Single.class) {
                single = new Single();
            }
        }

        return single;
    }
}
2.3、双重检测模式

这种模式是将上面懒汉模式进行的改良后的产物。经过·上面的分析,相信大家对懒汉模式的优缺点都已经很清楚了。但是上面的懒汉模式都不适合在实际开发中去使用。为了在实际开发中更好的使用,因此对上面的懒汉模式进行再次的改良。 2.3.1、代码实现

通过上面分析可以得出,加锁可以解决线程安全问题,但是如何合理的将锁应用到这个里面,从而使得该方法即可以解决多线程安全问题,还可以解决效率过低的问题。解决方案:首先通过if条件判断,从而得知成员doubleCheck是否被实例化。如果一个线程刚刚完成第一个条件判断时间片就用完了,当第二个线程完成实例化后,第一个线程再次获取到锁后,因为doubleCheck被volatile修饰,所以在第二个线程完成实例化后,当第一个线程再次获取到时间片后,就会检测到该成员已经实例化就不会再次实例,从而解决了多线程问题,同时当有其他线程再次运行该方法时,由于该成员已经被实例化了,从而就不会再次实例化,会直接返回已经实例化好的成员。从而也解决了效率太低的问题。

public class DoubleCheck {
    //注意,要加上volatile关键字
    private volatile static DoubleCheck doubleCheck;

    private DoubleCheck() {
    }

    
    private static DoubleCheck getInstance() {
        if (null == doubleCheck) {
            synchronized (DoubleCheck.class) {
                if (null == doubleCheck) {
                    doubleCheck = new DoubleCheck();
                }
            }
        }

        return doubleCheck;
    }
}
2.4、静态代码块模式

这种方式采用了类加载的机制来保证初始化实例时只有一个线程。静态内部类方式在StaticClass类被加载时并不会立即实例化,而是在需要实例化时,调用getInterface方法,才会装载SingleInstancce类,从而完成StaticClass实例化。类的静态属性只会在第一次加载类的时候初始化,所以在这里,JVM帮助我们保证了线程的安全性。在类初始化时,别的线程是无法进入的。优点:避免了线程不安全,利用静态内部类特点实现延迟加载,效率高。结论:推荐使用。 2.4.1、代码实现

public class StaticClass {

    private StaticClass() {
    }

    
    private static class StaticInstance {
        private static final StaticClass INSTANCE = new StaticClass();
    }

    
    private static StaticClass getInstance() {
        return StaticInstance.INSTANCE;
    }
}

2.5、枚举模式

这借助JDK1.5添加的枚举来实现单例模式。不仅能避免多线程同步问题,而且还能防止反序列化重现创建创建新的对象。这种方式是Effectiive Java作者Josh Bloch提倡的方式。结论:推荐使用 2.5.1、代码实现

public enum EnumWay {
    INSTANCE;

    public void method() {
        System.out.println("OK");
    }
}

欢迎分享,转载请注明来源:内存溢出

原文地址: https://www.outofmemory.cn/zaji/5721678.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-18
下一篇 2022-12-18

发表评论

登录后才能评论

评论列表(0条)

保存