• Java实现单例模式的两种方式


           单例模式在实际开发中有很多的用途,比如我们在项目中常用的工具类,数据库等资源的连接类。这样做的好处是避免创建多个对象,占用内存资源,自始自终在内存中只有一个对象为我们服务。

    单例对象一般有两种实现方式。懒汉式和饿汉式。

    饿汉式实现如下:

    package com.day05;
    
    /**
     * 饿汉式
     * 
     * @author Administrator
     *
     */
    public class Single {
        // 定义一个个私有静态本类对象变量,饿汉式是一上来就给该变量进行初始化,加上final是让s这个对象引用始终保持不变,更为严谨
        private static final Single s = new Single();
    
        // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
        private Single() {
        }
    
        // 暴露出一个外界可以获取该类对象的公共静态方法
        public static Single getInstance() {
            return s;
        }
    
    }

    测试类:

    package com.day05;
    
    public class SingleDemo {
        public static void main(String[] args) {
            Single s1 = Single.getInstance();
            Single s2 = Single.getInstance();
            System.out.println(s1==s2);
    
        }
    }

    运行结果:

    true
    即s1==s2说明了,s1和s2在内存中地址都相等,即s1、和s2是同一个对象。

    懒汉式实现如下:

    package com.day05;
    
    /**
     * 懒汉式
     * 
     * @author Administrator
     *
     */
    public class Single {
        // 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
        private static Single s = null;
    
        // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
        private Single() {
        }
    
        // 暴露出一个外界可以获取该类对象的公共静态方法
        public static Single getInstance() {
            if (s == null)
                s = new Single();
            return s;
        }
    
    }

    以上的代码如果是单线程的话就不会存在问题,但是当有多线程操作的时候,就会存在线程安全问题,演示代码如下:

    package com.day05;
    
    /**
     * 懒汉式
     * 
     * @author Administrator
     *
     */
    public class Single {
        // 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
        private static Single s = null;
    
        // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
        private Single() {
        }
    
        // 暴露出一个外界可以获取该类对象的公共静态方法
        public static Single getInstance() {
            if (s == null) {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                s = new Single();
            }
            return s;
        }
    
    }

    测试类如下:

    package com.day05;
    
    public class SingleDemo implements Runnable {
    
        public static void main(String[] args) {
            SingleDemo sd = new SingleDemo();
            new Thread(sd).start();
            new Thread(sd).start();
    
        }
    
        @Override
        public void run() {
            Single s = Single.getInstance();
            System.out.println(s);
    
        }
    }

    运行结果如下:

    com.day05.Single@4081b5a4
    com.day05.Single@64dcdaac

    可以发现每次运行结果打印出获取对象不是同一个,即存在线程安全问题。

    问题分析:

     由此我们可以采用Java给我们提供的同步锁来解决以上的问题,修改代码如下:

    package com.day05;
    
    /**
     * 懒汉式
     * 
     * @author Administrator
     *
     */
    public class Single {
        // 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
        private static Single s = null;
    
        // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
        private Single() {
        }
    
        // 暴露出一个外界可以获取该类对象的公共静态方法
        public static synchronized Single getInstance() {
            if (s == null) {
                try {
                    Thread.sleep(1000L);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                s = new Single();
            }
            return s;
        }
    
    }

    这样就解决了上面的代码存在的线程安全问题,但是同步锁虽然可以解决了线程安全问题,但是却会存在效率问题,所以我们可以采用双重判断的方法来优化一下改代码如下所示:

    package com.day05;
    
    /**
     * 懒汉式
     * 
     * @author Administrator
     *
     */
    public class Single {
        // 定义一个个私有静态本类对象变量,懒汉式是先赋值为null,当需要的时候在初始化
        private static Single s = null;
    
        // 构造方法私有化,让外部无法通过new 关键字来进行创建对象
        private Single() {
        }
    
        // 暴露出一个外界可以获取该类对象的公共静态方法
        // 这里需要注意的使静态共享数据使用的使该类的字节码对象即Single.class
        public static Single getInstance() {
            // 这里增加了一次判断,可以少一次进行锁的处理
            if (s == null) {
                synchronized (Single.class) {
                    if (s == null) {
                        try {
                            Thread.sleep(1000L);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        s = new Single();
                    }
                }
            }
    
            return s;
        }
    
    }

    总结:还是比较推荐使用饿汉式,因为写法简单,不存在线程安全问题。

  • 相关阅读:
    c++获取时间戳
    指针数组学习
    Matlab小波工具箱的使用2
    matlab 小波工具箱
    指针
    低通滤波参数
    git 合并分支到master
    matlab json文件解析 需要下载一个jsonlab-1.5
    matlab2017b
    数据结构-链式栈c++
  • 原文地址:https://www.cnblogs.com/nanyangke-cjz/p/7059060.html
Copyright © 2020-2023  润新知