java解惑,77--乱锁之妖
下面的这段程序模拟了一个小车间。程序首先启动了一个工人线程,该线程在停止时间到来之前会一直工作(至少是假装在工作),然后程序安排了一个定时器任务(timer task)用来模拟一个恶毒的老板,他会试图阻止停止时间的到来。最后,主线程作为一个善良的老板会告诉工人停止时间到了,并且等待工人停止工作。那么这个程序会打印什么呢?
<span style="font-size:18px;">public class Worker extends Thread{ private long count = 0; private volatile boolean quittingTime = false; public void run(){ while(!quittingTime){ pretendToWork(); } System.out.println("Beer is good"); } private void pretendToWork(){ try{ Thread.sleep(300); System.out.println("product:"+(count++)); }catch(InterruptedException ex){ } } //It's quitting time ,wait for worker-Called by good boss synchronized void quit() throws InterruptedException{ quittingTime = true; join(); } //Recsind quiting time-Called by evil boss synchronized void keepWorking(){ quittingTime = true; } public static synchronized void main(String[] args) throws InterruptedException{ final Worker worker = new Worker(); worker.start(); Timer t = new Timer(true); t.schedule(new TimerTask(){ public void run(){ worker.keepWorking(); } },500); Thread.sleep(400); worker.quit(); } } </span>
按照书上的解释:
其实,并没有什么可以保证上述几个交叉的事件会按照上面的时间轴发生。无论是Timer类还是Thread.sleep方法,都不能保证具有实时(real-time)性。这就是说,由于这里计时的粒度太粗,所以上述几个事件很有可能会在时间轴上互有重叠地交替发生。100毫秒对于计算机来说是一段很长的时间。此外,这个程序被重复地挂起;看起来好像有什么其他的东西在工作着,事实上,确实是有这种东西。
我们的分析存在着一个基本的错误。在500ms时,当作为恶毒老板的定时器任务运行时,根据时间轴的显示,它对keepWorking方法的调用会被阻塞,因为keepWorking是一个同步化的方法并且主线程正在同一个对象上执行着同步化方法quit(在Thread.join中等待着)。这些都是对的,keepWorking确实是一个同步化的方法,并且主线程确实正在同一个对象上执行着同步化的quit方法。即使如此,定时器线程仍然可以获得这个对象上的锁,并且执行keepWorking方法。这是如何发生的呢?
问题的答案涉及到了Thread.join的实现。这部分内容在关于该方法的文档中(JDK文档)是找不到的,至少在迄今为止发布的文档中如此,也包括5.0版。在内部,Thread.join方法在表示正在被连接(join)的那个Thread实例上调用Object.wait方法。这样就在等待期间释放了该对象上的锁。在我们的程序中,这就使得作为恶毒老板的定时器线程能够堂而皇之的将quittingTime重新设置成false,尽管此时主线程正在执行同步化的quit方法。这样的结果是,工人线程永远不会看到停止时间的到来,它会永远运行下去。作为善良的老板的主线程也就永远不会从join方法中返回了。
使这个程序产生了预料之外的行为的根本原因就是WorkerThread类的作者使用了实例上的锁来确保quit方法和keepWorking方法的互斥,但是这种用法与超类(Thread)内部对该锁的用法发生了冲突。
但是我多次运行后,均能得到正常结果。。。。。
希望有人解惑,或许是因为我的程序有问题吧
再来说说编写程序中遇到的小问题吧!
//public class Seventyseventh{ // static class Worker extends Thread{//需要写上static,否则的话,在main里定义work会出错。原因:无法从静态上下文中引用非静态 变量 // private long count = 0; // private volatile boolean quittingTime = false;//体会volatile有时并不能真正的实现锁机制 // public void run(){ // while(!quittingTime){ // pretendToWork(); // } // System.out.println("Beer is good"); // } // private void pretendToWork(){ // try{ // Thread.sleep(300); // System.out.println("product:"+(count++)); // }catch(InterruptedException ex){ // // } // } // //It's quitting time ,wait for worker-Called by good boss // synchronized void quit() throws InterruptedException{ // quittingTime = true; // join();//等待线程结束,也就是执行完run方法 // } // //Recsind quiting time-Called by evil boss // synchronized void keepWorking(){ // quittingTime = true; // } // } // public static synchronized void main(String[] args) throws InterruptedException{ // final Worker worker = new Worker();//必须是final类型,否则的话,在new TimeTask里调用失败,原因是从内部类中访问本地变量worker; 需要被声明为最终类型 // worker.start(); // Timer t = new Timer(true); // t.schedule(new TimerTask(){ // public void run(){ // worker.keepWorking(); // } // },500); // Thread.sleep(400); // worker.quit(); // } //}
原文地址:http://blog.csdn.net/havedream_one/article/details/44936853