请保证 多线程 下的 单例 2---3种方案(学习)
内容大多转载 csdn 此文章为学习专用 原博主文章路径,http://blog.csdn.net/u011518120/article/details/52922342
单例模式的分类
1 饿汉模式 2懒汉模式
饿汉模式:类初始化的时候就进行创建单例模式
懒汉模式:在调用getinstance方法的时候才创建单例;
前提 :
首先饿汉模式是不会出现任何问题的。而且在大多数情况下饿汉模式要由于懒汉模式。
只有在少数情况下,增快应用启动速度或者减少初始化延迟的时候才使用懒汉模式。
典型的懒汉模式的解决方案是DCL。除此之外还有一种方案是使用静态类初始化方案。
饿汉模式
饿汉模式很简单在静态域内进行初始化。不会出现任何问题
public class Student{ private volatile static Student stu = new Student(); private Student(){}; }
懒汉模式
在多线程的情况下可能会有多个线程同时调用getinstance这样会导致创建多个实例。
下面说 3种方案
synchronized
synchronized方法来同步方法或者同步整个getinstance。
当然是有效的只是效率比较低。
需要说明的是synchronized方法只对new方法进行同步是不可行的,因为在检测null之后可能会有线程切换。
DCL双重检测锁
首先对UniqueInstance引用加速volatile变量。
然后在在对new函数进行同步,并进行双重检测,这是目前通用的高效解决方案。
下面贴一下代码:
package test;
public class MyObject{
private volatile static MyObject myObject;
private MyObject(){};
public static MyObject getInstance(){
try{
if(myObject == null){
Synchronized(MyObject.class){
if(myObject == null){
myObject = new MyObejct();
}
}
}
}catch (InterruptedException e){
e.printStackTrace();
}
return myObject;
}
}
package test;
public class MyThread extends Thread{
@Override
public void run(){
System.out.println(MyObject.getInstance().hashCode());
}
}
public class Run{
public static void main(String[] args){
MyThread t1 = new MyThread();
MyThread t2 = new MyThread();
MyThread t3 = new MyThread();
t1.start();
t2.start();
t3.start();
}
}
类初始化的解决方案
从深层次的角度说之所以出现单例模式失效的原因不仅仅是因为线程间的切换,还有是因为编译器和处理器在执行java代码的时候会进行重排序。从而导致没有良好同步的语句之间代码的执行顺序不是按照程序员预想的那样。
使用类初始化的原理就是在执行类初始化的过程中,当前线程需要获得类初始化的锁。这样只有获得了类初始化的锁的程序才能进行初始化。及时在本线程内初始化的语句产生了重排序但是在获得锁和释放锁之间的程序属于临界区内,对外界而言是不可见的。因此也不会产生多个实例的结果。
简而言之,如果在类的内部构造一个静态的类,那这个类的初始化只会由一个获得了这个类初始化锁的进程执行。
代码如下:
package test;
public class InstanceFactory{
private static class InstanceHolder{
public static Instance instance = new Instance();
}
public static Instance getInstance(){
return InstanceHolder.isntance;
}
这样的类初始化方案显得比较简洁,但是相比于volatile的方案这样的初始化只能进行静态类变量的访问,对于实例变量的内容则无能无力了