一天一个设计模式——(Singleton)单例模式(线程安全性)

一、模式说明

  有时候,我们希望在应用程序中,仅生成某个类的一个实例,这时候需要用到单例模式。

二、模式类图

  

三、模式中的角色

  •   Singleton角色,该模式中仅有的一个角色,该角色有一个返回唯一实例的getInstance方法,该方法总是返回同一个实例;

四、代码示例

  单例模式比较简单,要实现单例模式只需保证三点:

  • 私有化类的构造函数;
  • 在类中创建类的对象;
  • 提供获取对象的公有方法;

package com.designpattern.cn.singletonpattern;

public class Singleton {
    private static Singleton singleton = new Singleton();

    //也可以使用静态域,在类加载时创建实例
    //static {singleton = new Singleton();}

    private Singleton(){
        System.out.println("Instance created!");
    }

    public static Singleton getInstance(){
        return singleton;
    }
}

测试类运行结果如下:

  上面的代码中,Singleton类的方法和成员属性都是静态的,原因是我们不能直接创建Singleton类的实例,但是想通过类的方法调用获取实例,因此方法必须是静态的,同时,静态方法只能操作静态成员,所以对象也是静态的。

  另外注意到,Singleton类的构造函数被设置为私有的,这样可以避免通过调用new方法来直接创建对象。

五、扩展——几种不同模式的单例模式实现及其线程安全性分析

  • 饿汉模式

  上面的代码示例中实现的单例模式被称为“饿汉模式”,因为类在加载的过程中,单例就已经初始化完成,确保在获取Instance的时候,实例是已经存在的了。因此饿汉模式是线程安全的单例模式实现,可以直接用于多线程环境。并且由于是在类的加载阶段就生成了实例,因此第一次调用获取对象方法时效率高。缺点是:无论程序最终是否会用到这个类,这个类都会被创建,会占用一定的内存。

  • 懒汉模式

  与饿汉模式相对的,如果我们在加载类的时候不创建实例,等到第一次调用getInstance方法时才生成实例对象,这种做法就是“懒汉模式”。

懒汉模式相比于饿汉模式,由于是在第一次调用getInstance方法时才通过构造函数创建对象,如果对象的创建代码比较复杂,会影响第一次获取对象的效率。同时,懒汉模式如果不做特殊处理,很明显是线程不安全的,如果要使用懒汉模式又想用在多线程环境中,有几种方式实现线程安全:

  1、第一个能想到的方法就是加锁。

package com.designpattern.cn.singletonpattern;

public class LazySingletonThreadLock {
    private static LazySingletonThreadLock lazySingletonThreadLock = null;
    private LazySingletonThreadLock(){};
    public static synchronized LazySingletonThreadLock getInstance(){
        if(lazySingletonThreadLock == null){
            lazySingletonThreadLock = new LazySingletonThreadLock();
        }
        return lazySingletonThreadLock;
    }
}

  这种写法的最大缺点就是效率低!每个线程在想获得类的实例调用getInstance()方法都要进行同步,实际上这个方法只执行一次实例化代码就够了,后面每次获取实例直接return就行了,不用每次都让方法同步。

2、第二个方法:双重校验锁

package com.designpattern.cn.singletonpattern;

public class LazySingletonDoubleCheck {
    //双重检查方式
    private static volatile LazySingletonDoubleCheck lazySingletonDoubleCheck = null;
    private LazySingletonDoubleCheck(){}
    public static LazySingletonDoubleCheck getInstance(){
        if(lazySingletonDoubleCheck == null){
            synchronized (LazySingletonDoubleCheck.class){
                if(lazySingletonDoubleCheck == null){
                    lazySingletonDoubleCheck = new LazySingletonDoubleCheck();
                }
            }
        }
        return lazySingletonDoubleCheck;
    }
}

  这种写法通过两次判断lazySingletonDoubleCheck成员是否为空,从而决定是否创建实例,一旦实例创建后,后续调用获取对象的方法时,直接返回对象,优点:线程安全、延迟加载、高效。

  这里必须要插播一下volatile这个java关键字:volatile关键字用以声明变量的值可能随时会别的线程修改,使用volatile修饰的变量会强制将修改的值立即写入主存,主存中值的更新会使缓存中的值失效。volatile具备可见性和有序性,不具备原子性。

  • 可见性:多个线程访问一个变量x,A线程修改了变量x的值,其他B线程、C线程。。。立即可以读取到A线程修改x后的值;
  • 有序性:即程序执行时按照代码书写的先后顺序执行。在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。而volatile会禁止指令重排,从而保证有序性。
  • 原子性:与事务相关的概念,表示一系列操作要么都执行,要么一个也不执行。如果只执行了一部分,那么需要回滚已执行的代码。olatile不具备原子性,这是volatile与java中的synchronized、java.util.concurrent.locks.Lock最大的功能差异。虽然volatile不能保证原子性,但是它也不阻塞线程,因此它的响应速度比synchronized快。volatile适用于读多写少的场景,或者对变量的写操作不依赖于当前值,对变量的读取操作不依赖于非volatile变量的场景。

3、第三种写法:静态内部类

package com.designpattern.cn.singletonpattern;

public class LazySingletonStaticInnerClass {
    private LazySingletonStaticInnerClass(){}
    //静态内部类
    private static class singletonInstance{
        private static final LazySingletonStaticInnerClass INSTANCE = new LazySingletonStaticInnerClass();
    }

    public static  LazySingletonStaticInnerClass getInstance(){
        return singletonInstance.INSTANCE;
    }
}

  这种写法比较推荐,这种方式看上去像是饿汉模式,实际上有区别,由于静态内部类的特性,在外部类加载时并不会马上实例化,只有在调用getInstance方法时,才会实例化类。这里,JVM帮助我们保证了线程安全性,当JVM进行类的初始化时,其他线程是无法进入的。保证了线程安全、延迟加载,效率高。

4、第四种写法:利用枚举类(实现单例模式的最佳做法)

  创建一个对象的方式有多种:new,克隆,序列化,反射。前三种情况,上面的饿汉模式和线程安全的懒汉模式都可以保证生成实例的唯一性,但是对于最后一种——反射,无法保证实例的唯一性:通过反射可以获取到类的构造方法(即使声明构造方法是private的也没用,反射可以打破一切封装,但是枚举例外)

package com.designpattern.cn.singletonpattern;

public enum SingletonEnum {
    INSTANCE;
}

  可以使用SingletonEnum.INSTANCE方式获取枚举的实例。由于枚举的构造方式和单例模式很像(构造方法私有化),而且不用考虑序列化等问题。因此使用枚举来构建单例模式是目前最好的做法,并且不被反射打破。(可以参考阅读《Effective Java》)

六、相关的模式

  • 抽象工厂(AbstractFactory)模式
  • Builder模式
  • Fcade外观模式
  • Prototype原型模式

原文地址:https://www.cnblogs.com/zheng-hong-bo/p/11087301.html

时间: 2024-08-29 01:10:42

一天一个设计模式——(Singleton)单例模式(线程安全性)的相关文章

一天一个设计模式(二) -单例模式(Singleton)

前言 单例模式 (Singleton) 是一种创建型模式,指某个类采用Singleton模式,则在这个类被创建后,只可能产生一个实例供外部访问,并且提供一个全局的访问点. 正文 (一). 优缺点 Java中单例模式 (Singleton) 是一种广泛使用的设计模式.单例模式的主要作用是保证在Java程序中,某个类只有一个实例存在.一些管理器和控制器常被设计成单例模式. 1. 优点 提供了对唯一实例的受控访问. 由于在系统内存中只存在一个对象,因此可以节约系统资源,对于一些需要频繁创建和销毁的对象

(转)设计模式之——单例模式(Singleton)的常见应用场景

单例模式(Singleton)也叫单态模式,是设计模式中最为简单的一种模式,甚至有些模式大师都不称其为模式,称其为一种实现技巧,因为设计模式讲究对象之间的关系的抽象,而单例模式只有自己一个对象,也因此有些设计大师并把把其称为设计模式之一. 这里又不具体讲如何实现单例模式和介绍其原理(因为这方便的已经有太多的好文章介绍了),如果对单例模式不了解的可以先看下:http://terrylee.cnblogs.com/archive/2005/12/09/293509.html .当然也可以自己搜索.

每天一个设计模式-4 单例模式(Singleton)

每天一个设计模式-4 单例模式(Singleton) 1.实际生活的例子 有一天,你的自行车的某个螺丝钉松了,修车铺离你家比较远,而附近的五金店有卖扳手:因此,你决定去五金店买一个扳手,自己把螺丝钉固定紧.不一会儿,自行车就被你修好了:首先,这个扳手你不会扔掉,下次用的时候直接找出来就用了.好,今天的主题出来了“单例模式”. 2.与变成关联 在上面的例子中,能找出几个关键字:“买一个扳手”,“螺丝钉固定紧”,“不会扔掉扳手”,“下次用直接找出来”.我们结合标题和这几个关键字好好理解一下: 买一个

设计模式01: Singleton 单例模式

Singleton 单例模式 动机(Motivation)当进行软件开发是会有这样一种需求:在系统中只有存在一个实例才能确保它们的逻辑正确性.以及良好的效率.这应该是类设计者的责任,而不是使用者的责任. 意图(Intent)保证一个类仅有一个实例,并提供一个该实例的全局访问点.——<设计模式>GoF 单线程Singleton模型实现: 1 pulic class Singleton 2 { 3 private static Singleton instance;//如果客户不需要这个实例最好不

设计模式之单例模式Singleton(三创建型)

1.什么事单例模式? 单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例. 单例模式有以下特点: 1.单例类只能有一个实例. 2.单例类必须自己创建自己的唯一实例. 3.单例类必须给所有其他对象提供这一实例. 单例模式主要分为:饿汉模式,懒汉模式. 饿汉式和懒汉式区别: 从名字上来说,饿汉和懒汉,饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例. 单例模式着

设计模式之单例模式——Singleton

                    设计模式之单例模式--Singleton 设计意图: 保证类仅有一个实例,并且可以供应用程序全局使用.为了保证这一点,就需要这个类自己创建自己的对象,并且对外有公开的调用方法.而且,别的类不能实例化它,所以构造方法要设置为私有的. 单例模式的要点 一是某个类只能有一个实例: 二是它必须自行创建这个实例: 三是它必须自行向整个系统提供这个实例. 例如: 有一个"单例对象",而"客户甲"."客户乙" 和&quo

设计模式之单例模式(singleton)

1.定义 确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例 2.通用类图 Singleton类称为单例类,通过使用private的构造函数确保了在一个应用中只产生一个实例,并且是自行实例化的(在Singleton中自己使用new Singleton()).通用源码如下所示: 饿汉模式: package singleton; public class Singleton { private static final Singleton singleton=new Singleton

java设计模式之单例模式(Singleton pattern)

java设计模式之单例模式(Singleton pattern) 单例模式的定义: Singleton pattern restricts the instantiation of a class and ensures that only one instance of the class exists in the java virtual machine. The singleton class must provide a global access point to get the i

C#设计模式之一单例模式(Singleton Pattern)【创建型】

原文:C#设计模式之一单例模式(Singleton Pattern)[创建型] 一.引言 看了李建忠老师的讲的设计模式已经有一段时间了(这段时间大概有一年多了),自己还没有写过自己的.有关设计模式的文章.这次想写一些关于设计模式的文章,用自己的理解和代码来写,算是复习一遍.写作的过程中也会多看看其他大牛的文章,争取让自己的理解正确,否则把大家带跑偏了,就是我的过错了.今天就开始我们第一个设计模式,该模式是:[单例模式],英文名称:Singleton Pattern,这个模式很简单,一个类型只需要