标签:trace ack 其他 实例化 不为 类名.class cat 等等 his
继 多线程编程[一]:http://www.cnblogs.com/wangfajun/p/6547648.html,我们开始第二篇文章啦。。。
上一篇中结尾,我们了解到,同步函数用的锁是 this ,那么我们接下来,在同步函数上加下个静态标示符static试试:
public class Test { public static void main(String[] args) { try { Ticket one = new Ticket(); new Thread(one).start(); Thread.sleep(10); one.flag = false; new Thread(one).start(); } catch (Exception e) { e.printStackTrace(); } } } class Ticket implements Runnable{ private static int ticket = 1000; boolean flag = true; @Override public void run() { if(flag){ synchronized(this){ while(true){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"同步代码块..."+ ticket--); } } } }else{ while(true) sale(); } } public static synchronized void sale(){ if(ticket>0){ System.out.println(Thread.currentThread().getName()+"同步方法..."+ ticket--); } } }
执行结果: .... 二号窗口卖票...2 二号窗口卖票...1 二号窗口卖票...0
好吧,又出现了0号票。线程又不安全了。思考线程安全的连个前提:
1.必须要有两个或以上的线程
2.必须是多个线程使用同一个锁
肯定是2没满足,那么,静态同步函数的锁对象不是this,是什么呢?
我们知道静态资源的特点:进内存的时候,内存中没有本类的对象,那么有谁?静态方法是不是由类调用的 ?类在进内存的时候,有对象吗? 有,就是那份字节码文件对象(Ticket.class),Ticket进内存,紧跟着,静态资源进内存,OK,我们来试试。。
将上面同步代码块中的this锁换成如下:
synchronized(Ticket.class){
while(true){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"同步代码块..."+ ticket--);
}
}
}
执行结果:
Thread-0同步代码块...5 Thread-0同步代码块...4 Thread-0同步代码块...3 Thread-0同步代码块...2 Thread-0同步代码块...1
最后一张为1号票,线程安全。
结论:静态同步函数使用的锁是该方法所在类的字节码文件对象,也就是 类名.class。
看完[一]再到这里,我们穿插一个小的知识点。单例模式,因为这个模式要学会了线程同步,才比较好讲这个知识点。
(好像刚出来的那会,面试中基本会问这个问题,或者笔试中让你写一个单利模式,有记忆吧。哈哈。。。青春一去不复返啊。。)
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 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都不为空了,直接返回,这样效率是不是提升了?
...回家吃饭了。。。明天继续。。
标签:trace ack 其他 实例化 不为 类名.class cat 等等 his
原文地址:http://www.cnblogs.com/wangfajun/p/6549870.html