当前位置:   article > 正文

设计模式-单例模式_李建忠设计模式单例模式

李建忠设计模式单例模式

1 单例模式

1.1 饿汉式

私有构造函数,禁止外部访问。
使用static和final做到类加载到内存后,就实例化一个单例,而且是唯一的,JVM保证线程安全。简单实用。
唯一缺点:不管用到与否,类装载时就完成实例化,占用内存。

1.1.1 写法1

package com.DesignPattern.singleton;

/*
饿汉式
类加载到内存后,就实例化一个单例,JVM保证线程安全
简单实用
唯一缺点:不管用到与否,类装载时就完成实例化
 */

public class Mgr01 {
    private static final Mgr01 INSTANCE = new Mgr01();//类加载时(static)就进行对象实例化(final),是唯一的

    private Mgr01(){};//私有构造函数,禁止外部访问

    // 被static关键字修饰的变量和方法统一属于类的静态资源,是类实例之间共享的。被static关键字修饰的变量、方法属于类变量、类方法,
    // 可以通过【类名.变量名】、【类名.方法名】直接引用,而不需要派生一个类实例出来。
    public static Mgr01 getInstance(){return INSTANCE;}

    public void m(){System.out.println("m");}//主方法

    // 测试一把,是否单例
    public static void main(String args[]){
        Mgr01 m1 = Mgr01.getInstance();
        Mgr01 m2 = Mgr01.getInstance();
        System.out.println(m1 == m2);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

1.1.2 写法2

用static语句块来做初始化

package com.DesignPattern.singleton;

/*
饿汉式
 */

public class Mgr02 {
    private static final Mgr02 INSTANCE;
    static {
        INSTANCE = new Mgr02();
    }

    private Mgr02() {
    }//私有构造函数,禁止外部访问

    public static Mgr02 getInstance() {
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }//主方法

    // 测试一把,是否单例
    public static void main(String args[]) {
        Mgr02 m1 = Mgr02.getInstance();
        Mgr02 m2 = Mgr02.getInstance();
        System.out.println(m1 == m2);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

1.2 懒汉式

在使用时再进行类的实例化。

1.2.1 写法1

使用if语句避免多次实例化,缺点是多线程访问时无法保证唯一性。

package com.DesignPattern.singleton;

/*
懒汉式
非线程安全
 */

public class Mgr03 {
    private static Mgr03 INSTANCE;

    private Mgr03() {
    }//私有构造函数,禁止外部访问

    public static Mgr03 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr03();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }//主方法

    // 测试一把,是否单例
    public static void main(String args[]) {
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr03.getInstance().hashCode());
                }
            }).start();
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

1.2.2 写法2

加锁保证线程安全,缺点是给整个获取实例的语句块加锁导致效率降低。

package com.DesignPattern.singleton;

/*
懒汉式
加锁保证线程安全
但整个语句块加锁导致效率降低
 */

public class Mgr04 {
    private static Mgr04 INSTANCE;

    private Mgr04() {
    }//私有构造函数,禁止外部访问

    public static synchronized Mgr04 getInstance() {
        if (INSTANCE == null) {
            try {
                Thread.sleep(5);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            INSTANCE = new Mgr04();
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }//主方法

    // 测试一把,是否单例
    public static void main(String args[]) {
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr04.getInstance().hashCode());
                }
            }).start();
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43

1.2.3 写法3

试图减小同步代码块以提高效率,下面这种写法不可行。不可行的原因是,if语句执行过的线程,都会进入执行同步语句块。if语句判断是否存在实例没有加锁,给执行语句块加锁没有意义。
所以这种写法是线程不安全的,加锁没有意义。

package com.DesignPattern.singleton;

/*
懒汉式
试图减小同步代码块以提高效率,这种写法不可行
 */

public class Mgr05 {
    private static Mgr05 INSTANCE;

    private Mgr05() {
    }//私有构造函数,禁止外部访问

    public static Mgr05 getInstance() {
        if (INSTANCE == null) {
            //试图通过减小同步代码块的方式提高效率,这种写法不可行
            synchronized (Mgr05.class){
                try {
                    Thread.sleep(5);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                INSTANCE = new Mgr05();
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }//主方法

    // 测试一把,是否单例
    public static void main(String args[]) {
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr05.getInstance().hashCode());
                }
            }).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

1.2.4 写法4

双重检查单例写法,懒汉式。是上一种写法的改良,相比写法2提升了效率,因为不是每个线程都需要加锁了,提高了并发。
在类被一个线程实例化之后,尚未进入第一层if的线程不会执行同步块语句;已经进入同步块之内的线程,执行完第二层if就会归还锁。

注意:为了多线程创建和访问的安全性,需要加volatile关键字。

package com.DesignPattern.singleton;

/*
懒汉式
同步代码块+双判断
线程安全且高效
 */

public class Mgr06 {
    private static volatile Mgr06 INSTANCE;

    private Mgr06() {
    }//私有构造函数,禁止外部访问

    public static Mgr06 getInstance() {
        //第一层用于提高效率,一旦类已被实例化,就不再往下执行
        if (INSTANCE == null) {
            synchronized (Mgr06.class){
                //第二层用于保证单例,防止进入第一层if的线程,获取锁之后再次实例化类
                if(INSTANCE==null){
                    try {
                        Thread.sleep(5);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    INSTANCE = new Mgr06();
                }
            }
        }
        return INSTANCE;
    }

    public void m() {
        System.out.println("m");
    }//主方法

    // 测试一把,是否单例
    public static void main(String args[]) {
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr06.getInstance().hashCode());
                }
            }).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48

1.3 静态内部类

1.3.1 写法1

在类的内部定义静态内部类,在该静态内部类中定义初始化的外部类的静态实例。
在获取外部类的实例的时候,返回静态内部类定义的经过初始化的外部类的静态实例。
由于加载外部类时不会加载内部类,这样可以实现懒加载。
JVM保证单例,因为JVM在加载类的时候只会加载一次。

package com.DesignPattern.singleton;

/*
静态内部类方式
JVM保证单例
由于加载外部类时不会加载内部类,这样可以实现懒加载
 */

public class Mgr07 {
    private Mgr07(){};//私有构造函数,禁止外部访问

    private static class Mgr07Holder{
        private static final Mgr07 INSTANCE = new Mgr07();
    }

    public static Mgr07 getInstance(){
        return Mgr07Holder.INSTANCE;
    }

    public void m(){System.out.println("m");}//主方法

    // 测试一把,是否单例
    public static void main(String args[]){
        for(int i=0;i<100;i++){
            new Thread(new Runnable() {
                @Override
                public void run() {
                    System.out.println(Mgr06.getInstance().hashCode());
                }
            }).start();
        }
    }
}

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

1.4 枚举实现

1.4.1 写法1

既能保证线程安全,也可以防止反序列化。(没有深究,这种写法没太理解)

package com.DesignPattern.singleton;

public enum  Mgr08 {
    INSTANCE;

    public void m(){}

    // 测试一把,是否单例
    public static void main(String args[]){
        for(int i=0;i<100;i++){
            new Thread(() -> System.out.println(Mgr08.INSTANCE.hashCode())).start();
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/秋刀鱼在做梦/article/detail/897277
推荐阅读
相关标签
  

闽ICP备14008679号