单例模式之懒汉单例(延迟初始化)多线程再解析

原创 2018年04月15日 16:51:22
单例模式之懒汉单例(延迟初始化)多线程再解析


1、多线程下的懒汉单例:
	public class Lazysingleton {
		private static Lazysingleton m_instance = null;

		// 私有默认构造方法,外界无法直接实例化
		private Lazysingleton() {
		}

		// 静态工厂方法
		public static Lazysingleton getInstance() throws InterruptedException {
			// 延迟加载
			if (m_instance == null) {
				// 模拟创建对象的准备工作
				Thread.sleep(3000);
				m_instance = new Lazysingleton();// 初始化这个单例
			}
			return m_instance;
		}
	}
	public class MyThread extends Thread {

		@Override
		public void run() {
			try {
				System.out.println(Lazysingleton.getInstance().hashCode());
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	public class TestLazy1 {

		public static void main(String[] args) {
			MyThread t1=new MyThread();
			MyThread t2=new MyThread();
			MyThread t3=new MyThread();
			
			t1.start();
			t2.start();
			t3.start();
		}
	}
    
打印结果说明创建出来三个对象,并不是单例,多线程下懒汉单例是非线程安全的。

多线程下单例模式非线程安全的解决方案:


1、声明synchronized关键字,实现同步方法

加入同步方法得到相同实例对象,此方法运行效率非常低,是同步运行,下一个线程想要取得对象,必须等上一个线程释放锁后,才能运行。


2、使用同步代码块

与使用synchronized同步方法一样是同步运行,效率非常低。得到相同实例对象。

3、部分代码上锁,进行单独同步,非线程安全


4、使用DCL双重检查锁定


使用DCL双重检查锁定成功解决懒汉模式的多线程问题,DCL也是大多数多线程结合单例模式使用的解决方案。

DCL是常见的延迟初始化技术,但有一个错误的用法。使用DCL需要一些技巧。

存在错误的根源:

a.多线程试图在同一时间创建对象,会通过加锁来保证只有一个线程创建对象。
b.在对象创建好后,执行getInstance()方法将不需要获取锁,直接返回创建好的对象。

问题:当代码读取到m_instance不为空,m_instance引用的对象有可能还没有完成初始化。就会出出现问题。

m_instance = new Lazysingleton();
可以分解为:
memory=allocate();1.分配对象的内存空间
ctorInstance(memory);2.初始化对象
instance=memory;3.设置instance指向刚分配的内存地址

在Java内存模型中为了优化代码会重排代码,会导致线程看到一个还没被初始化的对象。


线程安全的延迟初始化方案:

1、基于volatile的解决

声明volatile,初始化代码重排就会被禁止,此方案是通过禁止代码重排来实现线程安全的延迟加载。
创建对象的过程,实例化对象一般分为三个过程。
    1、分配内存空间。
    2 、初始化对象。
    3 、将内存空间地址赋值给对象的引用。
  但是由于重排序的缘故,步骤2、3可能会发生重排序,其过程如下
    1、分配内存空间
    2、将内存空间的地址赋值给对应的引用
    3、初始化对象
 如果不加volatile的话,可能线程1在初始化的时候重排序了,线程2看到singleton != null,已经返回singleton,其实线程1还没有完成初始化,仅仅只不过是分配了内存空间而已!


5、基于类初始化的解决方案(使用静态内置类实现单例)

除了使用DCL解决多线程单例模式的非线程安全的问题,使用静态内置类也可以实现同样的效果。










每天努力一点,每天都在进步。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/dingjianmin/article/details/79950352

实现类似Windows资源管理器的DataGrid

在DataGrid中,我们可以实现类似Windows资源管理器的效果,即对列进行排序,该列颜色与其它列不同。下面就是实现的代码:DataGridLikeWindowsExplorer.aspx@ Pa...
  • net_lover
  • net_lover
  • 2003-11-04 01:30:00
  • 3430

线程安全的单例懒汉模式(互斥锁)

线程安全的单例懒汉模式(互斥锁)//关于懒汉式的线程安全问题,使用同步机制: //对于一般的方法内,使用同步代码块,可以考虑使用this. //对于静态方法而言...使用当前类本身充当锁.class ...
  • s740556472
  • s740556472
  • 2017-01-06 13:40:34
  • 653

单例模式 (懒汉式, 线程同步详解)

单例模式(懒汉式)在懒汉式写法中, 我们需要非常注意线程同步的问题. 大概有一下几个: 1. getInstance() 直接锁方法好不好 2. 双重锁定 3. synchronized(thi...
  • u013647382
  • u013647382
  • 2016-04-13 10:44:54
  • 2341

C++单例模式(懒汉和饿汉)与线程安全

单例: 单例大约有两种实现方法:懒汉与饿汉。 懒汉:故名思义,不到万不得已就不会去实例化类,也就是说在第一次用到类实例的时候才会去实例化, 饿汉:饿了肯定要饥不择食。所以在单例类定义的时候就进行...
  • hj605635529
  • hj605635529
  • 2017-04-14 15:56:48
  • 643

两种单例模式比较(懒汉式、恶汉式)

懒汉式单例模式在类加载时不实例化该单例对象,在调用公共方法时才实例化,因此类加载速度快,运行时速度较慢。而饿汉式单例模式在类加载时就实例化该单例对象,因此类加载速度慢,运行时速度较快。...
  • kyyee
  • kyyee
  • 2015-12-23 10:24:45
  • 1731

单例模式深探-懒汉式和饿汗式

什么是单例模式 首先说句题外话,单例模式在很多的开源框架和项目都随处可见,所以单例模式的重要不言而喻,在一些稍微大点的公司设计模式肯定是会在面试中会问到的,单例模式的命中率不亚于工厂模式等设计模式(...
  • zcl1359205840
  • zcl1359205840
  • 2016-09-05 10:45:21
  • 591

单例设计模式详解一:不安全的懒汉式(多线程环境验证)

单例设计模式详解一:不安全的懒汉式(多线程环境验证)单例设计模式详解一不安全的懒汉式多线程环境验证 写在前面的话 饿汉式 懒汉式 验证在多线程环境下懒汉式单例写法的不安全之处 写在前面的话前言:虽然工...
  • lw_power
  • lw_power
  • 2016-11-23 15:52:17
  • 1740

Java设计模式之单例模式(恶汉式和懒汉式)

/*  * 单例模式:  *         饿汉式:类一加载就创建对象  *         懒汉式:用的时候,才去创建对象  * 面试题:单例模式的思想是什么?写一个代码体现(我们最好写懒...
  • u012110719
  • u012110719
  • 2015-05-02 08:47:38
  • 4025

单例模式中的懒汉模式和恶汉模式的区别

单例模式在我们开发中经常会用到的,不知道你所喜欢用饿汉模式还是喜欢懒汉模式呢?为什么会出现有两种方式来实现单例模式?我看这其中必蹊跷,你怎么看?大家都知道的是:懒汉模式会通过 判 null,然后 ne...
  • u012301841
  • u012301841
  • 2016-03-12 23:50:44
  • 7896

懒汉式单例模式的线程安全问题

新建一个单例模式类和一个多线程测试类 public class Test24 implements Runnable { public void run() { System.out.print...
  • u010584063
  • u010584063
  • 2015-07-11 09:24:58
  • 3739
收藏助手
不良信息举报
您举报文章:单例模式之懒汉单例(延迟初始化)多线程再解析
举报原因:
原因补充:

(最多只允许输入30个字)