详解单例模式

Posted mzcc

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了详解单例模式相关的知识,希望对你有一定的参考价值。

关于单例模式,话不多说,即程序运行时无论New了多少次,即内存中只有一个实例对象。即对象的HasHCode一致。

单例模式的两大类

1、饿汉模式(即加载时就创建对象)

  -1、直接实例化饿汉模式

  -2、静态代码块饿汉模式(即需要加载初始化配置的时候适用)

  -3、枚举方式

2、懒汉式(延迟加载)

  -1、单线程安全下的懒汉

  -2、多线程安全下的懒汉

  -3、静态内部类的懒汉(安全)

  


  1、直接实例化饿汉模式

  

package com.single;

public class Singleton {
   
    private static final Singleton SI=new Singleton();

    private Singleton(){

    }

    public static Singleton getsingleton(){
        return SI;
    }
}

 

  单例模式必须保证自行创建,并且内部提供一个静态变量来保存这个唯一的对象,构造器私有化,即其他类内部中无法直接New当前的单例类,还需要提供一个对外获取实例的方法

  当前模式在加载类的字节码的时候当前类的实例就立即会被创建

 

  2、静态代码块饿汉模式(即需要加载初始化配置的时候适用)

  

 1 public class Singleton2 {
 2 
 3     private static final Singleton2 SI;
 4 
 5     private String name;
 6     static {
 7         Properties properties=new Properties();
 8         try {
 9      
10             properties.load(Singleton2.class.getClassLoader().getResourceAsStream("db.properties"));
11             String name = properties.getProperty("name");
12             SI=new Singleton2(name);
13         } catch (IOException e) {
14             throw new RuntimeException(e);
15         }
16 
17     }
18 
19     private Singleton2(String name){
20         this.name=name;
21     }
22 
23     public static Singleton2 getsingleton(){
24         return SI;
25     }

 当我们需要在类创建的时候初始化参数,并且加载某些配置文件的时候可以使用 静态代码块的方式,在创建实例的时候通过构造器来完成相关参数的初始化。

 3.枚举方式的单例,自JDK1.5之后,首先枚举类和普通类的区别是什么?

使用enum定义的枚举类默认继承了java.lang.Enum类

枚举类的构造器只能使用private

枚举类的每个实例必须在枚举类中显示的列出(,分隔   ;结尾) 列出的实例系统会自动添加public static final修饰

所有的枚举类都定义了一个values方法,该方法可以很方便的遍历所有的枚举值

可以在switch表达式使用枚举类对象作为表达式,case子句可以直接使用枚举的名字,无需添加枚举类作为限定

枚举类对象的属性不能更改,所以要用private final修饰

枚举类对象要在构造器中被赋值
---------------------
作者:weirdowang
来源:CSDN
原文:https://blog.csdn.net/weirdowang/article/details/79970673

package com.single;
/*
枚举方式的单例
 */
public enum SingEnum {
    SING;
    SingEnum(){

    }
    public void info(){
        System.out.println("显示");
    }
}

  是不是很简洁呢? 确实如此,单例模式下 枚举方式的单例是最简洁的

  二、懒汉模式

 


  1、单线程安全下的懒汉

  

package com.single2;
/*
    懒汉模式
 */
public class Singleton {
    private static Singleton singleton;

    private Singleton(){
        System.out.println("创建");
    }

    public static Singleton getSingleton(){
        if (singleton==null){
            singleton=new Singleton();
        }
        return singleton;
    }


}

 

  我们可以看出无论怎么样,当前的懒汉都是在调用的时候才被加载创建的,但是它只是在单线程的情况下是安全的为什么呢? 现在有一个需求即多个线程对这个 单例进行访问构建对象,在构建对象的时候使当前线程短暂的休眠一下

package com.single2;
/*
    懒汉模式测试线程不安全
 */
public class Singleton2 {
    private static Singleton2 singleton;

    private Singleton2(){
        System.out.println("创建");
    }

    public static Singleton2 getSingleton() throws InterruptedException {
        if (singleton==null){
            Thread.sleep(100);
            singleton=new Singleton2();
        }
        return singleton;
    }


}

测试代码如下:

  

  /*
    懒汉模式下的多线程不安全
     */
    public static void main(String[] args) {
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                Singleton2 singleton1 = Singleton2.getSingleton();
                System.out.println(singleton1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Singleton2 singleton2 = Singleton2.getSingleton();
                    System.out.println(singleton2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        thread.start();
        thread2.start();
    }

  经过测试,多线程情况下这种是不安全的 为什么呢?  第一个线程进入 进去之后, 线程休眠100ms 在此期间,第二个线程也开始进去到了创建的方法,由于当前第一个线程正在休眠,所以当前单例为null ,然后第一个线程休眠结束,创建第一个对象,此后第二个线程还在if中,也相继创建对象,此时便构造成了线程不安全的懒汉单例

  2、线程安全的懒汉单例(双端检索)

package com.single2;

/*
懒汉模式 双端检索,避免了多次线程等待
 */
public class Singleton4 {
    private static Singleton4 singleton4;

    private Singleton4() {

    }

    public static Singleton4 getSingleton4() {
        if (singleton4 == null) {
            synchronized (Singleton4.class) {
                if (singleton4 == null) {
                    try {
                        Thread.sleep(100);

                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    singleton4 = new Singleton4();
                }
            }
        }
        return singleton4;
    }

}
使用synchronized 保证当前只能有一个线程进入 ,并且在外再次进行判断 若当前的单例已经被创建过了,避免了再次加锁,直接返回 
3、静态内部类的懒汉单例:(线程安全的)
package com.single2;

public class Singleton5 {
    private Singleton5(){

    }

    private static class demo{
        private static final Singleton5 SINGLETON_5=new Singleton5();
    }

    public static Singleton5 singleton5(){
        return demo.SINGLETON_5;
    }


}

也是懒汉模式最简单的单例实现,静态内部类不会随着外部类的初始化而初始化,并且内部类具有自己的类加载器,是安全的

如上便是单例模式的六种创建方式,欢迎大牛指正,源码放到群里了  另外也欢迎各位朋友们一起学习交流, qq群:956809929

 

 

以上是关于详解单例模式的主要内容,如果未能解决你的问题,请参考以下文章

单例模式详解

单例模式详解

JAVA设计模式(6:单例模式详解)

详解单例模式

Java设计模式图文代码案例详解Java五大创建者模式 建造者原型(抽象)工厂单例模式

14-java的单例设计模式详解