转自:https://blog.youkuaiyun.com/u010274580/article/details/77387397
1. 饿汉
//线程安全
public class SingletonClass{
//【静态常量,可用】
private static final SingtonClass instance = new SingletonClass();
private SingletonClass(){}
pubilc static SingletonClass getInstance(){
return instance;
}
}
public class SingletonClass{
//【静态代码块,可用】
private SingtonClass instance = null;
static{
instance = new SingletonClass();
}
private SingletonClass(){}
pubilc static SingletonClass getInstance(){
return instance;
}
}
分析:基于classloder机制避免了多线程的同步问题,
单例在加载时已经实例化,此时线程还没有启动,所以天生的线程安全,
无论这个类是否被使用,都会被创建一个instance。
2,懒汉
// 线程不安全
public class SingletonClass{
//【线程不安全,不可用】
private static SingletonClass instance = null;
private SingletonClass(){}
public static SingletonClass getInstance(){
if(instance == null){
//多线程时,在该处时间片切换,会发生错误;
instance = new SingletonClass();
}
return instance;
}
}
分析:线程A希望使用SingletonClass,调用getInstance()方法。因为是第一次调用,A就发现instance是null的,于是它开始创建实例,就在这个时候,CPU发生时间片切换,线程B开始执行,它要使用SingletonClass,调用getInstance()方法,同样检测到instance是null(注意,这是在A检测完之后切换的,也就是说A并没有来得及创建对象)因此B开始创建。B创建完成后,切换到A继续执行,因为它已经检测完了,所以A不会再检测一遍,它会直接创建对象。这样,线程A和B各自拥有一个SingletonClass的对象——单例失败!
//线程安全
public class SingletonClass{
//【线程安全,不推荐使用】
private static SingletonClass instance = null;
private SingletonClass(){}
public synchronized static SingletonClass getInstance(){
if(instance==null){
instance = new SingletonClass();
}
return instance;
}
//public static SingletonClass getInstance(){
// synchronized(SingletonClass.class){
// if(instance==null){
// instance = new SingletonClass();
// }
// }
// return instance;
//}
}
分析:sychronized对整个方法进行加锁,严重影响程序性能。
3.双重校验锁
public class SingletonClass{
//【推荐使用】
private volatile static SingletonClass instance = null;
public static SingletonClass getInstance(){
//double-check
if(instance==null){
synchronized(SingletonClass.class){
if(instance==null)
instance = new SingltonClass();
}
}
return instance;
}
private SingletonClass(){}
}
分析:所谓编译,就是把源代码“翻译”成目标代码(大多数是指机器代码)的过程。针对Java,它的目标代码不是本地机器代码,而是虚拟机代码。编译原理里面有一个很重要的内容是编译器优化。所谓编译器优化是指,在不改变原来语义的情况下,通过调整语句顺序,来让程序运行的更快。这个过程成为reorder。创建一个变量需要哪些步骤呢?
1. 申请一块内存,调用构造方法进行初始化操作,
2. 分配一个指针指向这块内存。
这两个操作谁在前谁在后呢?JVM规范并没有规定。那么就存在这么一种情况,JVM是先开辟出一块内存,然后把指针指向这块内存,最后调用构造方法进行初始化。
下面我们来考虑这么一种情况:线程A开始创建SingletonClass的实例,此时线程B调用了getInstance()方法,首先判断instance是否为null。按照我们上面所说的内存模型,A已经把instance指向了那块内存,只是还没有调用构造方法,因此B检测到instance不为null,于是直接把instance返回了——问题出现了,尽管instance不为null,但它并没有构造完成,就像一套房子已经给了你钥匙,但你并不能住进去,因为里面还没有收拾。此时,如果B在A将instance构造完成之前就是用了这个实例,程序就会出现错误了!
4.静态内部类
//线程安全,不受JDK版本影响,推荐使用
public class SingletonClass{
//【推荐使用】
private static class SingletonClassInstance{
private static final SingletonClass instance = new SingletonClass();
}
public static SingletonClass getInstance(){
return SingletonClassInstance.instance;
}
private SingletonClass(){}
}
分析:这一版本的单例模式实现代码中,使用了Java的静态内部类。这一技术是被JVM明确说明了的,因此不存在任何二义性。在这段代码中,因为SingletonClass没有static的属性,因此并不会被初始化。直到调用getInstance()的时候,会首先加载SingletonClassInstance类,这个类有一个static的SingletonClass实例,因此需要调用SingletonClass的构造方法,然后getInstance()将把这个内部类的instance返回给使用者。由于这个instance是static的,因此并不会构造多次。由于SingletonClassInstance是私有静态内部类,所以不会被其他类知道,同样,static语义也要求不会有多个实例存在。并且,JSL规范定义,类的构造必须是原子性的,非并发的,因此不需要加同步块。同样,由于这个构造是并发的,所以getInstance()也并不需要加同步。
5.枚举
public enum SingletonClass{
//【推荐使用】
INSTANCE;
public void method(){}
}
分析:这种方式是Effective Java作者Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还能防止反序列化重新创建新的对象.由于1.5中才加入enum特性,用这种方式写不免让人感觉生疏,在实际工作中,我也很少看见有人这么写过。
注意
1)如果单例由不同的类加载器装入,那便可能存在多个单例类的实例。
2)如果Singleton实现了java.io.Serializable接口,那么这个类的实例就可能被序列化和反序列化,可能存在多个单例类的实例。
对第一个问题修复的办法是:
private static Class getClass(String classname) throws ClassNotFoundException {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
if(classLoader == null)
classLoader = Singleton.class.getClassLoader();
return (classLoader.loadClass(classname));
}
对第二个问题修复的办法是:
public class Singleton implements java.io.Serializable {
public static Singleton INSTANCE = new Singleton();
protected Singleton() { }
private Object readResolve() {
return INSTANCE;
}
}
参考链接:
http://devbean.blog.51cto.com/448512/203501/
http://www.blogjava.net/kenzhh/archive/2013/03/15/357824.html