zoukankan      html  css  js  c++  java
  • 单例模式介绍及其线程安全问题

     

        介绍下单例模式,即保证对一个类只实例化一个对象。实际生产例子有,Spring的bean默认创建模式等。

      单例模式的组成:包括一个私有的构造器,一个私有的静态变量,一个公有的静态方法。单例模式本身很简单,主要复杂点是在它在线程并发下的如何保证 线程安全+资源消耗少 的问题。

     

      一.饿汉式单例(线程安全)

     

    缺点:直接实例化,资源会浪费。丢失了延迟实例化的性能好处。

     

     二.懒汉式单例(线程不安全)

     

     缺点:线程不安全,如果多个线程能够同时进入 if (instance == null) ,并且此时 instance 为 null,那么会有多个线程执行 instance = new LazySingleton(); 语句,这将导致实例化多次对象。即此类不是单例了。

     

    三.线程安全的懒汉式单例例子。

      1>  对 getInstance() 方法加锁,那么在一个时间点只能有一个线程能够进入该方法,从而避免了实例化多次 uniqueInstance。

    缺点:会有线程堵塞,性能上不好,不推荐。

     

     

     2>  双重校验锁。先判断 instance 是否已经被实例化,如果没有被实例化,那么才对实例化语句进行加锁

    缺点:基本无缺点,除了第一次实例化的时候会加锁,可能会有线程堵塞。

      如果两个线程都执行了 if 语句,那么两个线程都会进入 if 语句块内。虽然在 if 语句块内有加锁操作,但是两个线程都会执行instance = new Singleton(); 这条语句,只是先后的问题,那么就会进行两次实例化。因此必须使用双重校验锁,也就是需要使用两个 if 语句。

      instance 采用 volatile 关键字修饰也是很有必要的。volatile的作用是 1.内存可见性: 所有线程都能看到共享内存的最新状态  2.防止指令重排。 (volatile修饰的变量并不是原子变量。只是变量的读和写变成了原子操作)

       instance = new LazySingleton(); 这段代码其实是分为三步执行:

    1. 为 instance 分配内存空间
    2. 初始化 instance 
    3. 将 instance 指向分配的内存地址

    但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1>3>2。指令重排在单线程环境下不会出现问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getInstance() 后发现 instance 不为空,因此返回 instance ,但此时 instance 还未被初始化。

    使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行。

     

     

     3>静态内部类实现。具有延迟初始化的好处,而且由 JVM 提供了对线程安全的支持。

    内部静态类的变量并不会在类加载的时候就初始化好,而是会在调用的时候才初始化。保证资源的节约。

     

  • 相关阅读:
    【原创】主机不能访问虚拟机CentOS7中的站点
    phpStudy中MySQL版本升级到5.7.17方法
    phpStudy for Linux (lnmp+lamp一键安装包)
    Linux的wget命令详解【转载】
    重置密码解决MySQL for Linux错误 ERROR 1045 (28000): Access denied for user 'root'@'localhost' (using password: YES)
    mysql5.7密码过期ERROR 1862 (HY000): Your password has expired. To log in you must change
    电赛菜鸟营培训(二)——STM32F103CB之中断控制
    电赛菜鸟营培训(零)——Keil环境搭建
    电赛菜鸟营培训(一)——STM32F103CB之LED控制
    AppInventor学习笔记(四)——打地鼠应用学习
  • 原文地址:https://www.cnblogs.com/liumz0323/p/10424789.html
Copyright © 2011-2022 走看看