单例模式

作者: 司鑫 | 来源:发表于2016-12-21 20:01 被阅读84次

    1 简介


    单例模式是一种对象创建型模式。单例模式保证一个类只有一个实例存在,并且同时提供一个全局的方法进行进行访问该实例。

    应用场景

    • 在多个线程之间共享或对同一个对象进行操作时

    • 用作全局变量时

    • 大规模系统中为了提高性能,减少对象的创建,节省创建时间

    2 几种实现方式


    2.1 饿汉式

    所谓饿汉式,就是不管三七二十一,一上来就开干。不管你需不需要创建某个对象,只要在加载类的时候就创建。

    我要吃吃吃吃

    ** - 实现方式**

    实体类:Person.class

    public class Person {
        private String name;
        private Integer age;
        <!-在Person被加载的时候Person对象就被创建-!>
        private static Person person = new Person();
    <!--使用private ,防止用户利用空参构造器进行实例化--!>
        private Person(){
            
        }
        public static Person getPerson(){
            return person;
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        
    }
    

    MainClass.class

    public class MainClass {
        public static void main(String[] args) {
            Person person = Person.getPerson();
            person.setAge(20);
            person.setName("HAH");
            System.out.println(person.getName());
        }
    }
    

    在类加载时就实例化对象,这样可以保证该对象的唯一性,是线程安全的,但是相对的效率可能会降低,因为用户也许就不需要实例化该对象。

    2.2 懒汉式

    所谓懒汉式,顾名思义,就是用到的时候在创建。也就是说,在类加载的时候不会实例化对象,只有当用户真正的要创建对象的时候再调用方法实例化对象。


    哪位湿兄可以帮我盖下被子QAQ

    - 实现方式

    Peison.class

    public class Person {
        private String name;
        private Integer age;
        <!--类加载时不会实例化对象--!>
        private static Person person = null;
        private Person(){
            
        }
    <!-用户第一次调用getPerson时会创建对象,以后都不需要创建-!>
        public static Person getPerson(){
            if(person == null){
                person = new Person();
            }
            return person;
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }   
    }
    

    可见,使用懒汉式可以提高程序的灵活性。但是这样也存在一个很大的缺陷。当第一次调用getPerson( )方法时有多个线程同时访问时,那么就会出现线程同步问题。所以的我们需要在方法上加上synchronized防止多个线程同时访问。

    ** - 改进**

    Person.class

    public class Person {
        private String name;
        private Integer age;
        
        private static Person person = null;
        private Person(){
            
        }
    <!-在方法上加上synchronized保证当前方法同一时间点只能有一个线程访问-!>
        public static synchronized Person getPerson(){
            if(person == null){
                person = new Person();
            }
            return person;
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        
    }
    

    在给getPerson( )方法上加上synchronized可以避免多个线程第一次同时访问实例化多个对象,但是这样在方法上加上synchronized会大大的降低程序的效率,因为只有第一次调用getPerson( )方法时才会发生线程问题,而第二次及以后调用均不会发生线程问题,这样当有一个线程调用getPerson( )方法时,其它线程都将被阻塞,直到调用方法的线程调用完毕。

    2.3 双重检查

    双重检查既是在懒汉式的改进基础上进行改进,对实例化对象的代码加锁并加以判断,从而实现线程安全并且和懒汉式相比效率也大大提高。

    - 具体实现

    Person.class

    public class Person {
        private String name;
        private Integer age;
        
        private static Person person = null;
        private Person(){
            
        }
        public static  Person getPerson(){
            if(person == null){                               (1
    <!--将实例化对象的代码加锁--!>
                synchronized(Person.class){      (2
    <!-防止第一次调用getPerson方法时多个线程同时进入 (1,当第一线程创建完对象释放锁后,
    后面的线程获得锁多次创建对象-!>
                    if(person == null){
                        person = new Person();
                    }
                }
                
            }
            return person;
        }
        
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
        public Integer getAge() {
            return age;
        }
        public void setAge(Integer age) {
            this.age = age;
        }
        
    }
    

    3 三种实现方式的比较


    • 饿汉式和双重检查是线程安全的,而未改进的懒汉式不是线程安全的。

    • 改进后的懒汉式效率比较低,因为给方法加上了synchronized,导致了同一时刻只能有一个线程访问该方法,造成其它线程堵塞。

    • 双重检查相对于饿汉式来说灵活性更高,只有用户用到该对象的时候才会实例化,相对于懒汉式效率更高,用户只有第一次访问该,方法才会进入锁,其它线程可能会堵塞,但是以后每次方法都可以并发,线程不会再被阻塞。

    最近刚学设计模式,哪理解错了,望各位湿兄大佬多多指正

    上一篇:抽象工厂模式
    下一篇:原型模式

    相关文章

      网友评论

        本文标题:单例模式

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