zoukankan      html  css  js  c++  java
  • Java单例模式的几种常见实现方式

    Java单例模式的几种常见实现方式

    懒汉or饿汉?

    • 懒汉:单例模式延迟加载方式,一开始不给你new对象,你要new再给你new
    • 饿汉:上来就给你new好一个对象,你可以直接用(吃),你也没法new

    饿汉:不加锁,线程安全,用起来方便,容易产生垃圾对象

    public class SingletonTest1 {
        public static void main(String[] args) {
            //由于构造方法是私有的,则外界无法实例化该类
            //Singleton1 singleton1 = new Singleton1();
            new Thread(() -> {
                Singleton1 singleton1 = Singleton1.getInstance();
            }).start();
    
            Singleton1 singleton2 = Singleton1.getInstance();
        }
    }
    
    class Singleton1 {
        //类加载时直接通过声明调用构造方法初始化
        private static volatile Singleton1 singleton1 = new Singleton1();
    
        //让构造函数为private,这样该类不会被外部调用实例化对象
        private Singleton1(){
            check();
        }
    
        //通过静态方法提供外界获取该唯一可用对象的窗口
        //直接给你用,你也别想着什么时候创建了
        public static Singleton1 getInstance() {
            return singleton1;
        }
    
        //第一次创建实例就打印
        private static void check() {
            System.out.println("第一次创建该类的唯一实例才会打印这句话");
        }
    }
    

    输出:

    第一次创建该类的唯一实例才会打印这句话
    

    单线程下的单例模式(懒汉,线程不安全)

    这种方式只能在单线程环境中实现单例模式

    public class SingletonTest1 {
        public static void main(String[] args) {
            //由于构造方法是私有的,则外界无法实例化该类
            //Singleton1 singleton1 = new Singleton1();
    
            //只可以通过实例化的方式
            Singleton1 singleton1 = Singleton1.getInstance();
            Singleton1 singleton2 = Singleton1.getInstance();
            singleton1.test();
        }
    }
    
    class Singleton1 {
        //默认初始化为null
        private static Singleton1 singleton1;
    
        //让构造函数为private,这样该类不会被外部调用实例化对象
        private Singleton1(){}
    
        //通过静态方法提供外界获取该唯一可用对象的窗口
        //如果唯一的实例没有创建就负责创建,否则返回唯一的对象引用
        public static Singleton1 getInstance() {
            if (singleton1 == null) {
                check();
                singleton1 = new Singleton1();
            }
            //第二次创建就不会新建一个对象,而是传回之前已经创建的对象实例
            return singleton1;
        }
    
        //一个实例方法,用于证明实例化成功
        public void test() {
            System.out.println("测试方法,证明确实获得到了唯一实例");
        }
    
        //第一次创建实例就打印
        private static void check() {
            System.out.println("第一次创建该类的唯一实例才会打印这句话");
        }
    }
    

    输出:

    第一次创建该类的唯一实例才会打印这句话
    //显然第二次获取实例时只是获得到了之前创建的实例
    测试方法,证明确实获得到了唯一实例
    

    多线程下的单例模式(一)(懒汉,线程安全)

    区别在于将方法上锁(这里也可以采用重入锁),我们来分析一下具体的工作环境,A线程为了检测是否新建的了实例,对get方法上锁,开始检测对象是否为null,并调用构造方法,返回对象...B线程试图中途获得锁被阻塞,然后等A线程释放锁之后,B获得锁去检测是否创建了对象,发现已经创建,就返回对象释放锁...

    public class SingletonTest1 {
        public static void main(String[] args) {
            //由于构造方法是私有的,则外界无法实例化该类
            //Singleton1 singleton1 = new Singleton1();
            new Thread(() -> {
                Singleton1 singleton1 = Singleton1.getInstance();
            }).start();
    
            Singleton1 singleton2 = Singleton1.getInstance();
        }
    }
    
    class Singleton1 {
        //默认初始化为null
        private static Singleton1 singleton1;
    
        //让构造函数为private,这样该类不会被外部调用实例化对象
        private Singleton1(){}
    
        //通过静态方法提供外界获取该唯一可用对象的窗口
        //如果唯一的实例没有创建就负责创建,否则返回唯一的对象引用
        public synchronized static Singleton1 getInstance() {
            if (singleton1 == null) {
                check();
                singleton1 = new Singleton1();
            }
            //第二次创建就不会新建一个对象,而是传回之前已经创建的对象实例
            return singleton1;
        }
    
        //一个实例方法,用于证明实例化成功
        public void test() {
            System.out.println("测试方法,证明确实获得到了唯一实例");
        }
    
        //第一次创建实例就打印
        private static void check() {
            System.out.println("第一次创建该类的唯一实例才会打印这句话");
        }
    }
    

    输出:

    第一次创建该类的唯一实例才会打印这句话
    

    多线程下的单例模式(二)(双重检测)

    这里要注意:上锁解锁的很消耗时间空间的,而这里无论对象是否存在,都先上锁,再去检测,显然是没有必要的,当线程很多的时候会造成资源大量的浪费~可以先检测对象是否存在,如果对象不存在再试图申请锁去创建资源,则会节约资源并达到线程安全的目的——双重检测

    public class SingletonTest1 {
        public static void main(String[] args) {
            //由于构造方法是私有的,则外界无法实例化该类
            //Singleton1 singleton1 = new Singleton1();
            new Thread(() -> {
                Singleton1 singleton1 = Singleton1.getInstance();
            }).start();
    
            Singleton1 singleton2 = Singleton1.getInstance();
        }
    }
    
    class Singleton1 {
        //默认初始化为null,volatile:主内存 工作内存
        private static volatile Singleton1 singleton1;
    
        //让构造函数为private,这样该类不会被外部调用实例化对象
        private Singleton1(){}
    
        //通过静态方法提供外界获取该唯一可用对象的窗口
        //如果唯一的实例没有创建就负责创建,否则返回唯一的对象引用
        public static Singleton1 getInstance() {
            if (singleton1 == null) {
                synchronized (Singleton1.class) {
                    //两个贤臣可能都越过了外层的null检测,所以可能进入后已经不是null了
                    if (singleton1 == null) {
                        check();
                        singleton1 = new Singleton1();
                    }
                }
            }
            return singleton1;
        }
    
        //一个实例方法,用于证明实例化成功
        public void test() {
            System.out.println("测试方法,证明确实获得到了唯一实例");
        }
    
        //第一次创建实例就打印
        private static void check() {
            System.out.println("第一次创建该类的唯一实例才会打印这句话");
        }
    }
    
  • 相关阅读:
    loadrunner监控linux之linux下安装rpc
    Linux中top命令参数详解
    使用jmeter监控服务器性能指标
    jmeter连接mysql数据库配置
    loadrunner--设置代理录制
    页面下载时间细分图组成
    linux网络配置
    科学把妹法
    简单而强大的bitset
    名言札记
  • 原文地址:https://www.cnblogs.com/YLTFY1998/p/14331825.html
Copyright © 2011-2022 走看看