zoukankan      html  css  js  c++  java
  • Java设计模式-单例模式及线程安全问题

    单例模式是非常常用的设计模式,他确保了一个类只有一个对象,并且这个对象是自己创建的,外界可以获取使用到这个对象。


    单例模式一般有两种:懒汉式,饿汉式(其实还有一种登记式,把创建的对象放在map集合中,有就直接用,没有就创建)

    单例模式通过构造方法私有化,外界无法创建对象,下面是两种单例的实现

    饿汉式:

    package demo_singleton;
    
    public class SingletonHungry {
        private static SingletonHungry singletonhunary = new SingletonHungry();
    
        public static SingletonHungry getinstance() {
            return singletonhunary;
        }
    
        private SingletonHungry() {
    
        }
    
    
    }
    

    懒汉式:

    package demo_singleton;
    /*懒汉式的单例模式,有线程安全问题,当多线程访问的时候,会出现多个实例*/
    public class SingletonLazy {
        private static SingletonLazy instance = null;
    
        public static SingletonLazy getInstance() {
            if (instance == null) {
                instance = new SingletonLazy();
            }
            return instance;
        }
    
        private SingletonLazy() {
    
        }
    }
    

    多线程环境下的测试:

    懒汉式测试:

    package demo_singleton;
    
    public class TestLary extends Thread{
        public static void main(String[] args) {
            TestLary t1 = new TestLary();
            TestLary t2 = new TestLary();
            TestLary t3 = new TestLary();
            t1.start();
            t2.start();
            t3.start();
        }
    
        public void run() {
            System.out.println(SingletonLazy.getInstance());
        }
    }
    

    输出结果:

    demo_singleton.SingletonLazy@529e3fc2
    demo_singleton.SingletonLazy@529e3fc2
    demo_singleton.SingletonLazy@136c03ee

    通过输出结果可以看到,其实创建了两个对象

    饿汉式测试:

    package demo_singleton;
    
    public class TestHungry extends Thread {
        public static void main(String[] args) {
            TestHungry t1 = new TestHungry();
            TestHungry t2 = new TestHungry();
            TestHungry t3 = new TestHungry();
            t1.start();
            t2.start();
            t3.start();
        }
    
        @Override
        public void run() {
            System.out.println(SingletonHungry.getinstance());
        }
    
    }
    

    输出结果:

    demo_singleton.SingletonHungry@6d15a113
    demo_singleton.SingletonHungry@6d15a113
    demo_singleton.SingletonHungry@6d15a113
    在多线程下饿汉式没有出现问题

    在单线程的环境下,单例可以实现,当在多线程的条件下,懒汉式的单例就会出现线程安全问题,而饿汉式不会出现。饿汉式代码简单所以线程安全,在类加载的时候创建对象,懒汉式不会再在载的时候创建对象,效率高。代码中有判断对象是否存在的代码,所以线程不安全饿汉式不需要关注多线程问题,写法简单,但是有个缺点,就是不管你用或者不用,在类加载的时候他都会创建对象,而懒汉式的特点是延时加载,但是又带来了线程问题。既然这样我们就要解决它

    开始解决懒汉式的问题,最开始想到的是同步,加上synchronized关键字:

    优化一:

    package demo_singleton;
    
    public class SingletonLazy1 {
        private static SingletonLazy1 instance1 = null;
    
        public static synchronized SingletonLazy1 getInstance() {
            if (instance1 == null) {
                instance1 = new SingletonLazy1();
            }
            return instance1;
        }
    
        private SingletonLazy1() {
    
        }
    }
    

    优化二:


    加入同步块,对代码进行操作

    package demo_singleton;
    /*加同步块的单例,效率有问题*/
    
    public class SingletonLazy2 {
        private static SingletonLazy2 instance2 = null;
    
        public static SingletonLazy2 getInstance() {
            synchronized (SingletonLazy2.class) {
                if (instance2 == null) {
                    instance2 = new SingletonLazy2();
                }
            }
            return instance2;
        }
    
        private SingletonLazy2() {
    
        }
    }
    

    优化三:

    package demo_singleton;
    
    public class SingletonLazy3 {
        private static SingletonLazy3 instace3 = null;
    
        public static SingletonLazy3 getInstance() {
            if (instace3 == null) {
                synchronized (SingletonLazy3.class) {
                    if (instace3 == null) {
                        instace3 = new SingletonLazy3();
                    }
                }
            }
            return instace3;
        }
    
        private SingletonLazy3() {
    
        }
    }
    

    相对于前两个优化,利用同步解决线程问题,第三个利用两个判断,当程序判断对象没有创建的时候进入执行同步代码块,创建对象。在下次判断对象存在的时候,不会再执行同步代码块中的代码,相对于前两个效率会搞一些

    优化四:

    package demo_singleton;
    
    public class SingletonLazy4 {
        private static SingletonLazy4 instance4 = null;
        static{
            instance4 = new SingletonLazy4();
        }
    
        public static SingletonLazy4 getInstance() {
    
            return instance4;
        }
    
        private SingletonLazy4() {
    
        }
    }
    

    利用static静态代码块的特点,创建对象,static代码块在类加载的时候最开始执行并且只会执行一次

    优化五:

    package demo_singleton;
    
    public class SingletonLazy5 {
        private static class Sing {
            private static SingletonLazy5 instance = new SingletonLazy5();
        }
    
        public static SingletonLazy5 getInstance() {
            return Sing.instance;
        }
    
        private SingletonLazy5() {
    
        }
    }
    

    利用静态内部类,内部类在编译的时候也是一个单独的class文件,在调用的时候会执行,不调用的时候不会执行

    注意:利用Java的反射机制也是可以破坏单例的


    参考:http://blog.csdn.net/cselmu9/article/details/51366946

  • 相关阅读:
    make_blobs(源码)
    odoo13
    Odoo Community & Enterprise | & 我的一些瞎扯。。
    odoo 应用笔记 01 [草稿]
    PVE 为虚拟机 直通硬盘
    freebsd 断电 单用户模式恢复
    odoo 绿色版
    WSL 手动下载安装
    Command pg_dump not found
    odoo性能优化
  • 原文地址:https://www.cnblogs.com/duzhentong/p/7816586.html
Copyright © 2011-2022 走看看