zoukankan      html  css  js  c++  java
  • 设计模式之——单例模式

    引言

      设计模式分为三种类型:

      1)创建者模式:单例模式、抽象工厂模式、原型模式、建造者模式、工厂模式

      2)结构型模式:适配器模式、桥接模式、装饰模式、组合模式、外观模式、享元模式、代理模式

      3)行为型模式:模板方法模式、命令模式、访问者模式、迭代器模式、观察者模式、中介者模式、备忘录模式、解释器模式、状态模式、策略模式、职责链模式(责任链模式)

    一、单例模式

      所谓的单例模式,就是采取一定的方法保证在整个软件系统中,对于某个类只能存在一个实例对象,并且该类只对外提供一个获取该对象的方法(静态方法)

      单例模式有七种方式:

      1)饿汉式(静态常量)

      2)饿汉式(静态代码块)

      3)懒汉式(线程不安全)

      4)懒汉式(线程安全,同步方法)

      5)双重检查

      6)静态内部类

      7)枚举

     1)饿汉式(静态常量)【主】

    public class singleton1 {
        public static void main(String[] args) {
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            System.out.println(singleton1 == singleton2);
        }
    }
    class Singleton{
        private Singleton(){}//构造器私有化
        private final static Singleton singleton= new Singleton();
        public static Singleton getInstance() {
            return singleton;
        }
    }

      这种方式在类初始化时就会完成 “单例对象” 的实例化,避免了线程安全问题。但同时这也是他的缺点,假如此类还有其他静态方法和属性,可能调用方调用的并不是getInstance方法,并不会使用此 “单例对象”,但是调用了类的其他静态方法非final成员势必导致类的初始化,而一旦类初始化,就会初始化此 “单例对象”,这会造成一定的空间浪费,没有达到懒加载的效果。

     2)饿汉式(静态代码块)【次】

    public class singleton2 {
        public static void main(String[] args) {
            Singleton singleton1 = Singleton.getInstance();
            Singleton singleton2 = Singleton.getInstance();
            System.out.println(singleton1 == singleton2);
        }
    }
    class Singleton{
        private Singleton(){}
        private final static Singleton singleton;
      //final修饰的变量可以在静态代码块、代码块和构造器中赋值
    static { singleton = new Singleton(); } public static Singleton getInstance() { return singleton; } }

     3)懒汉式(线程不安全)【次】

    
    

    public class singleton1 {
    public static void main(String[] args) {
      for(int i=0;i<20;i++) {
        new Thread(() -> {
          Singleton singleton2 = Singleton.getInstance();
          System.out.println(singleton2);
        },"Thread"+i).start();
        }
      }
    }
    class Singleton{
      private Singleton(){}
      private static Singleton singleton;
      public static Singleton getInstance() {
      if(singleton == null)
        return singleton = new Singleton();
        return singleton;
      }
    }

     

      这样的方式是线程不安全的,假设现在有两个线程A、B,线程A在进入 if(singleton == null)后并没有来得及创建对象,CPU就将资源转给了线程B,因为线程A此前并没有将类成员singleton实例化,所以线程B进入了if后创建了对象并返回,此时A线程拿到资源后加载上下文继续创建了对象。由此可知在多线程的情况下这种方式并不安全。所以在实际开发中不要使用这种方式。

    com.qlu.singleton.Singleton@10d49a2
    com.qlu.singleton.Singleton@10d49a2
    com.qlu.singleton.Singleton@1fae4985
    com.qlu.singleton.Singleton@10d49a2
    .
    .
    .

     4)懒汉式(线程安全,同步方法)【主】

    public class singleton1 {
        public static void main(String[] args) {
            for(int i=0;i<20;i++) {
                new Thread(() -> {
                    Singleton singleton2 = Singleton.getInstance();
                    System.out.println(singleton2);
                },"Thread"+i).start();
            }
        }
    }
    class Singleton{
        private Singleton(){}
        private static Singleton singleton;
        public static synchronized Singleton getInstance() {//加锁
            while(singleton == null)
                return singleton = new Singleton();
            return singleton;
        }
    }

      但是这样效率比较低,不推荐使用。

     5)双重检查

    public class singleton1 {
        public static void main(String[] args) {
            for(int i=0;i<20;i++) {
                new Thread(() -> {
                    Singleton singleton2 = Singleton.getInstance();
                    System.out.println(singleton2);
                },"Thread"+i).start();
            }
        }
    }
    class Singleton{
        private Singleton(){}
        private static volatile Singleton singleton;
        public static synchronized Singleton getInstance() {
            if(singleton == null) {
                synchronized (Singleton.class) {
                    if(singleton == null) {
                        return singleton = new Singleton();
                    }
                }
            }
            return singleton;
        }
    }

      这种方式是线程安全的,假设现在有两个线程A、B,线程A在进入 if(singleton == null)后并没有来得及创建对象,CPU就将资源转给了线程B,因为线程A此前并没有将类成员singleton实例化,所以线程B进入了if后创建了对象并返回,此时A线程拿到资源后加载上下文开始if判断,由于B已经创建对象,所以并不能执行if里面的代码。由此可知在多线程的情况下这种方式是安全的,所以在实际开发中推荐使用这种方式。

     6)静态内部类【推荐使用】

    public class singleton1 {
        public static void main(String[] args) {
            for(int i=0;i<20;i++) {
                new Thread(() -> {
                    Singleton singleton2 = Singleton.getInstance();
                    System.out.println(singleton2);
                },"Thread"+i).start();
            }
        }
    }
    class Singleton{
        private Singleton(){}
        
        private static class SingletonInstance{
            private static final Singleton INSTANCE = new Singleton();
        }
        public static Singleton getInstance() {
            return SingletonInstance.INSTANCE;
        }
    }

     7)枚举

    public class singleton1 {
        public static void main(String[] args) {
            for(int i=0;i<20;i++) {
                new Thread(() -> {
                    Singleton singleton2 = Singleton.INSTANCE;
                    singleton2.say();
                },"Thread"+i).start();
            }
        }
    }
    enum Singleton{
        INSTANCE;
        public void say() {
            System.out.println("hello");
        }
    }

    二、JDK中使用的单例模式

     饿汉式的RunTime:

     

    三、单例模式的应用场景

     1)单例模式的应用变量为什么要倍设计成static修饰呢?类本身多外暴露的接口时static的

     2)单例模式保证了系统内存(方法区)中该类只要一个对象,节省了系统资源,对于一些需要频繁创建销毁的对象,使用单例模式可以提高系统的性能,对于一些经常使用到的 “重量级” 的对象、工具类对象、频繁访问数据库或文件系统的对象,使用单例模式也是很好的选择。

  • 相关阅读:
    深入浅出Win32多线程程序设计【2】线程控制
    深入浅出Win32多线程程序设计【1】基本概念
    在两个ASP.NET页面之间传递值
    Javascript基础
    DataGrid的几个小技巧
    推荐取代Visio的中国人的软件——Edraw
    ASP.NET如何防范SQL注入攻击
    软件版本号规定原则
    三层体系结构总结(三)
    .Net工具 .NET文档生成工具2.2
  • 原文地址:https://www.cnblogs.com/superlsj/p/11699935.html
Copyright © 2011-2022 走看看