单例模式(懒汉模式)
2016-08-23 本文已影响0人
dependmyse
package com.exam.test;
/**
* 懒汉式单例
* Created by xiangyang.laixiang on 2016/8/23.
*/
public class SingleInstance {
private static SingleInstance singleInstance;
/**
* 懒汉式单例,非线程安全
* @return
*/
public static SingleInstance getSingleInstance(){
if(singleInstance == null)
{
singleInstance = new SingleInstance();
}
return singleInstance;
}
/**
* 懒汉式单例,线程安全
* 这是实现线程安全最简单的方式,但这里会导致一个性能问题,因为我们把整个getInstance同步起来了,
* 这就会导致每时每刻都只能有一个线程调用getInstance方法,而同步只发生在第一次声明的时候
* 这就引出了双重检验锁的概念
* @return
*/
public synchronized static SingleInstance getSingleInstance2(){
if(singleInstance == null)
{
singleInstance = new SingleInstance();
}
return singleInstance;
}
}
/**
* 双重检验锁
*/
class DoubleCheckSingleInstance{
//使用volatile关键字修饰
//private static DoubleCheckSingleInstance singleInstance;
private static DoubleCheckSingleInstance singleInstance;
public static DoubleCheckSingleInstance getSingleInstance()
{
if(singleInstance == null)
{
synchronized (DoubleCheckSingleInstance.class)
{
if(singleInstance == null)
{
singleInstance = new DoubleCheckSingleInstance();
}
}
}
return singleInstance;
}
/**
* 上述这段代码看起来很完美,但是中间存在着一个问题那就是 new DoubleCheckSingleInstance()
* 这个操作不是原子操作,大致有三步构成。
* 1. 给instance分配内存
* 2. 调用DoubleCheckSingleInstance构造函数初始化成员变量
* 3. 将instance指向内存
* 问题就出在这一部分,jvm即时编译器中存在着指令重排序的优化。如果1,2,3的执行步骤不会存在问题
* 倘若1,3,2
* 那么当线程二执行完第三步以后,线程三抢占cpu资源,则进行检查,发现instance不为空,则返回,但此时
* 成员变量尚未初始化,则会发生调用错误。
* 解决方案是使用volatile来修饰singleInstance来强制每次都从内存映像中读取数据,其实volitile也可以起到
* 禁止重排序的功能,在java1.5以后使用比较安全。(1.5之前的内存模型是存在问题的)
*/
}
/**
* effective java中推荐的内部类实现单例的写法 。
*/
class SingtonInstance{
private SingtonInstance()
{
System.out.println("constructor");
}
private static class SingtonHolder{
private static SingtonInstance instance = new SingtonInstance();
}
public static void beforeInvoke()
{
System.out.println("before invoke");
}
public static SingtonInstance getInstance()
{
/**
* 此处最开始理解有点绕,但是经过我对象的一个提醒恍然明白,对于内部类的所有对象对于其
* 外部类来说都是可见的,简直是霸气,否则private的可见范围是不允许直接访问的。
*/
return SingtonHolder.instance;
}
}
public class Test {
public static void main(String[] args) {
SingtonInstance.beforeInvoke();
SingtonInstance.getInstance().beforeInvoke();
}
}