• 单例模式


    什么单例模式?

     单例模式是一种对象创建型模式,使用单例模式,可以保证为一个类只生成唯一的实例对象。也就是说,在整个程序空间中,该类只存在一个实例对象。其实,GoF对单例模式的定义是:保证一个类、只有一个实例存在,同时提供能对该实例加以访问的全局访问方法。

    为什么要使用单例模式?

    在应用系统开发中,我们常常有以下需求:

    - 在多个线程之间,比如servlet环境,共享同一个资源或者操作同一个对象

    - 在整个程序空间使用全局变量,共享资源

    - 大规模系统中,为了性能的考虑,需要节省对象的创建时间等等。

    因为Singleton模式可以保证为一个类只生成唯一的实例对象,所以这些情况,Singleton模式就派上用场了。

    怎么实现模式?

    1.饿汉式。

     1 public class Person {
     2     //用static修饰因为方法是静态的需要调用,final是因为变量只会被赋值一次,保证单例
     3     public static final Person person = new Person();
     4     private String name;
     5     
     6     public String getName() {
     7         return name;
     8     }
     9 
    10     public void setName(String name) {
    11         this.name = name;
    12     }
    13     
    14     //构造函数私有化保证不能实例化对象
    15     private Person() {
    16     }
    17     
    18     //提供一个全局的静态方法
    19     public static Person getPerson() {
    20         return person;
    21     }
    22 }

    2.懒汉式。

    ---多线程环境下不能保证单例唯一

     1 public class Person2 {
     2     private String name;
     3     private static Person2 person;
     4     
     5     public String getName() {
     6         return name;
     7     }
     8 
     9     public void setName(String name) {
    10         this.name = name;
    11     }
    12     
    13     //构造函数私有化
    14     private Person2() {
    15     }
    16     
    17     //提供一个全局的静态方法
    18     public static Person2 getPerson() {
    19         if(person == null) {    //*如果两个线程都在此处进入,就会创建两个对象,保证不了单例
    20             person = new Person2();
    21         }
    22         return person;
    23     }
    24 }

    ---使用同步方法,保证单例唯一,但是锁住了整个方法,而我们只需要锁住“new 对象()”这段,所以会影响到性能

     1 public class Person3 {
     2     private String name;
     3     private static Person3 person;
     4     
     5     public String getName() {
     6         return name;
     7     }
     8 
     9     public void setName(String name) {
    10         this.name = name;
    11     }
    12     
    13     //构造函数私有化
    14     private Person3() {
    15     }
    16     
    17     //提供一个全局的静态方法,使用同步方法
    18     public static synchronized Person3 getPerson() {
    19         if(person == null) {
    20             person = new Person3();
    21         }
    22         return person;
    23     }
    24 }

    3.双重检查。

    为什么要双重检查?

    当两个线程都一起通过第一个“person=null”的时候,线程1执行同步代码块的代码,创建对象,    但是因为线程2也已经通过第一个“person=null”判断条件,如果没有再加一层判断,也会创建另一个对象,造成非单例。这时需要在同步代码块加一层判断,线程1执行完对象的创建,这是person就不为空了,以后的线程通不过第二个if条件,自然也就保证了单例。

    public class Person4 {
        private String name;
    
        /**
         * 使用volatile 避免指令重排序
         */
        private static volatile Person4 person;
        
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
        
        //构造函数私有化
        private Person4() {
        }
        
        //提供一个全局的静态方法
        public static Person4 getPerson() {
    
            /**
             * JVM 存在指令重排序
             * 1 memory = alloc()    分配对象的内存空间
             * 2 initInstance()      初始化对象
             * 3 instance = memory   设置instance指向刚分配的内存
             *
             * JVM优化指令重排
             *
             * 1 memory = alloc()    分配对象的内存空间
             * 3 instance = memory   设置instance指向刚分配的内存
             * 2 initInstance()      初始化对象
             *
             *
             *
             */
            if(person == null) {
                // 双重检测机制     B 到这里,发现对象已经分配成功,
                // 对象还没初始化一旦调用,发现返回的对象为空,还没初始化完毕
                synchronized (Person4.class) {
                    if(person == null) {
                        person = new Person4();  // A - 3
                    }
                }
            }
            return person;
        }
    }

    主函数:

    public class MainClass {
        public static void main(String[] args) {
            //饿汉模式
            Person per1 = Person.getPerson();
            Person per2 = Person.getPerson();
            per1.setName("rr");
            per2.setName("meimei");
        
            System.out.println(per1.getName());
            System.out.println(per2.getName());
            
            System.out.println("-------------------");
            //懒汉模式1
            Person2 per3 = Person2.getPerson();
            Person2 per4 = Person2.getPerson();
            per4.setName("rr");
            per4.setName("meimei");
        
            System.out.println(per3.getName());
            System.out.println(per4.getName());
            
            System.out.println("-------------------");
            //懒汉模式2
            Person3 per5 = Person3.getPerson();
            Person3 per6 = Person3.getPerson();
            per5.setName("rr");
            per6.setName("meimei");
        
            System.out.println(per5.getName());
            System.out.println(per6.getName());
            
            System.out.println("-------------------");
            //双重判断
            Person4 per7 = Person4.getPerson();
            Person4 per8 = Person4.getPerson();
            per7.setName("rr");
            per8.setName("meimei");
        
            System.out.println(per7.getName());
            System.out.println(per8.getName());
            
            System.out.println("-------------------");
            
        }
    }
  • 相关阅读:
    方法指针或非指针类型接收器
    error接口
    17、想要回到项目上一版本,或者指定版本时,如何进行操作?
    08、想要找到所有操作记录时,如何操作
    16、不再追踪时,如何实现撤销追踪操作
    1月19日a
    1月18日
    1月20日
    1月25
    1月17日
  • 原文地址:https://www.cnblogs.com/Jemb/p/6754544.html
Copyright © 2020-2023  润新知