zoukankan      html  css  js  c++  java
  • 详解单例模式

    关于单例模式,话不多说,即程序运行时无论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
    牛客网 反转链表 JAVA
    牛客网 调整数组顺序使奇数位于偶数前面 JAVA
    Integer to Roman LeetCode Java
    Valid Number leetcode java
    Longest Common Prefix
    Wildcard Matching leetcode java
    Regular Expression Matching
    Longest Palindromic Substring
    Add Binary LeetCode Java
  • 原文地址:https://www.cnblogs.com/Mzcc/p/10747692.html
Copyright © 2011-2022 走看看