很多地方我们要求一个对象存在一个。原因是这个对象在系统中需要保留连续的状态。从能用到好用到耐用,这种思路是我们应该学习的。
1.可用的线程不安全方法
这个实现在多线程调用的环境中因为没有同步,很有可能出现多次创建。线程不安全。
2.粗暴但是耐用的线程安全的方法
getInstance变为 public static sychronized getInstance(){}
这里面可以保证前程安全。
但是sychronized开销过大,会使得效率降低100倍以上。如果不是在多线程的环境中,使用第一种方法就够了。
解决这种同步的低效还有一种方法,但是会浪费空间。这也是一种空间换时间的折中吧。
3.使用早创建来代替懒创建
懒创建就是如同上面的办法,在判断为null的时候才进行创建。
public class Singleton{
private static Singleton uniqueInstance=new Singleton();
private Singleton();
public static Singleton getInstance(){
return unniqInstance;
}
}
这个方法可以保证线程安全。
早创建将对象的实例化写在了类的声明中。这样在类加载到JVM中的时候就会实例化对象,这是在任何线程访问这个类之前的。只有在卸载的改类的时候该实例的空间才会被释放。
(
插播一段java类加载和卸载的知识:
java的类加载后且当使用阶段完成之后,java类就进入了卸载阶段,也就是所谓的释放。
使用阶段包括主动引用和被动引用,主动饮用会引起类的初始化,而被动引用不会引起类的初始化。
一个java类的完整的生命周期会经历加载、连接、初始化、使用、和卸载五个阶段,当然也有在加载或者连接之后没有被初始化就直接被使用的情况,如图所示:
PS:关于类的卸载,在类使用完之后,如果满足下面的情况,类就会被卸载:
该类所有的实例都已经被回收,也就是java堆中不存在该类的任何实例。
加载该类的ClassLoader已经被回收。
该类对应的java.lang.Class对象没有任何地方被引用,无法在任何地方通过反射访问该类的方法。
如果以上三个条件全部满足,jvm就会在方法区垃圾回收的时候对类进行卸载,类的卸载过程其实就是在方法区中清空类信息,java类的整个生命周期就结束了。
)
4.使用双重检查锁来代替synchronize
我们发现方法2中,因为synchronize修饰的是getInstance,所以无论是否需要创建新的实例,都需要以为该关键字而损失效率。而我们仔细分析问题会发现,只有在判断引用为空,并需要创建实例的时候才可能出现多线程不安全,实例多次创建的情况。所以仅需在判断引用为空的时候使用关键字。
这种方法是来实现getInstance方法是最好的。可以大大减少方法的开销,同时保证线程安全。
这里面之所以进行两边uniqueInstance==null的判断,并不是冗余。
第一,实例化之前对于是否为null的判断一定是要在synchronized中才有效。所以里面要判断一下才能实例化。因为同步块以外的判断并不能用来决断接下来的状态,同步块里面的判断在同步块中保持稳定,是null就是null,而不会被其他线程悄悄改变。
第二,如果只有上面的判断就和方法2中的低效线程安全一样了。所有第一次非同步的判断是用来过滤不需要同步的情况的。因为如果不为null则压根不需要创建对象,也不需要同步。而使用中从这条判断路径为大多数,而判断为null流程指向初始化的则只有一次。大多数都不需要加锁,只有第一次才要,这就是这种方法提高效率的原因。
(
插播知识
1.volatile关键字 经常被用来修饰一个变量或者引用,来保证不同线程对于该引用具有最新的可见性。之所以之前的方法没有用是因为对于静态方法同步相当于类同步,类中的对象是不可以同时被不同线程访问的,所以uniqueInstance引用也不会被多线程同时访问。而本例中getInstance方法并没有同步,类中的引用uniqInstance可以同时被多个线程读取和赋值,所以这时候加上volatile关键字可以较高效的使之对于不同线程可见。
2.synchronize的除了修饰方法的其他用法。http://blog.csdn.net/yaerfeng/article/details/7254734
这里提炼出来学习的药店。
a.synchronize一定是对于某一个对象(java中基本类型的数组也是对象)加锁。只有同一个实例在不同的线程访问的时候才会进行加锁。而同一个类的不同对象在不同线程访问的时候是不影响的。
synchronize(Obj obj){
}
b.synchronize修饰方法的时候加锁的是这个方法的宿主对象。而对于静态函数的加锁等同于对于类名字面常量加锁,
即 synchronized(Singleton.class){
}这种形式。这种形式是对类的加锁,即使是同一个类的不同的对象访问,也会进行加锁同步。
)