美文网首页@IT·互联网
java设计模式-单例模式及其几种实现方式

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

作者: methon | 来源:发表于2020-08-25 21:23 被阅读0次

    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更为完美,推荐使用。

    相关文章

      网友评论

        本文标题:java设计模式-单例模式及其几种实现方式

        本文链接:https://www.haomeiwen.com/subject/nhiajktx.html