继 多线程编程-实现及锁机制:http://www.cnblogs.com/wangfajun/p/6547648.html,我们开始第二篇文章啦。。。
穿插一个小的知识点。单例模式,因为这个模式要学会了线程同步,才比较好讲这个知识点。
(好像刚出来的那会,面试中基本会问这个问题,或者笔试中让你写一个单利模式,有记忆吧。哈哈。。。青春一去不复返啊。。)
1饱汉模式:
class Single{ public static final Single single = new Single(); public static Single getInstance(){ return single; } }
初始化时,就给你创建了一个实例化对象,不会出现线程安全问题,再看看懒汉模式:
2..懒汉模式(延迟加载):
class Single{ public static Single single = null; public static Single getInstance(){ if(single == null){ single = new Single(); } return single; } }
假如现在有A、B两个线程,同时访问getInstance方法时,分析下执行过程:
1.A线程进入if条件判断,发现single为null,然后线程A挂在这了,
2.B线程进来了,然后也挂这了,
3.A线程活了,继续往下执行,new了一个Single对象出来了
4.B线程也活了,往下执行又创建了一个Single对象
这样是不是线程又不安全了?没错。。。。听明白了否?
如何解决?修改代码如下:
class Single{ public static Single single = null; public static synchronized Single getInstance(){ if(single == null){ single = new Single(); } return single; } }
加了一个 synchronized 来修饰,每个线程想要获得这个Single实例的时候,都要判断锁,我们发现,线程安全了。。。
不过虽然这样解决了线程安全问题,但是getInstance()方法效率比较低,如何提高效率?接着修改代码:
class Single{ public volatile static Single single = null; public static Single getInstance(){ if(single==null){ synchronized(Single.class){ if(single == null){ single = new Single(); } } } return single; } }
将同步函数变成同步代码块,然后再在同步代码块外包了一层if条件判断,为什么这么做就提升了效率?假设现在有A、B.等等多个线程,再来分析下执行过程:
1.A线程进入synchronized方法块中,获得了锁,挂这了
2.B线程进入最外层if条件判断,single为null,满足,继续执行,发现锁被A线程拿着了,进不去,B线程挂这了
3.A线程活了,进入内层if条件判断,single为null,满足,new了一个Single对象出来,释放了锁,此时single已经被实例化了
4.B线程活了,进入了synchronized方法块种,内层if条件判断,不满足,执行return single.
后续的其他线程进来,判断最外层if条件判断,single都不为空了,直接返回,这样效率是不是提升了?
还有一种方式:静态内部类
public class InnerClass { private static class Single{ private static Single single = new Single(); } private static Single getInstance(){ return Single.single; } }
了解以上这些,我来问几个问题:
1.懒汉式有什么特点?
延迟加载
2.懒汉模式有没有问题?
当多线程访问的时候,会出现线程安全问题
3.如何解决?
加同步函数或者同步代码块,不过会出现效率问题,可以加双重判断的方式来解决。
4.加同步的时候,使用的锁是哪一个?
该类所属的字节码所属对象
介绍了上面两种模式,接下来再介绍一种单例模式(枚举)
public class Single { //不允许外部创建实例 private Single(){} public static Single getInstance(){ return SingleEnum.INSTANCE.getInstance(); } private enum SingleEnum{ INSTANCE; private Single single = null; SingleEnum(){ single = new Single(); } public Single getInstance(){ return single; } } }
这种模式相对于懒汉模式更加快速安全,相对于饱汉模式,更加节省资源。
构造函数SingleEnum永远只会被调用一次。