java多线程初步-原子性可见性hanppened-before原则
在看《how works tomcat》时穿插看了《Java多线程并发编程实现》,现在俩本书都渐入尾声...
《Java多线程并发编程实现》觉得理解的还不够,准备看第二遍,然后再看看《java并发编程:设计原则与模式》..《how works tomcat》准备结合源码,尝试编写简单的web服务器,这个周期会很长,给自己定的半年的时间吧..
分享下看书的心得把,我觉得写博客是很好的学习方式,不再拿书上的例子,例子全部为自己重写,这样才能引发更多的思考。
看下面这个例子,Plane是一个存储飞机状态的类,它有俩个状态变量想想x,y来表示其坐标,Diasphater线程会不停轮流调用Plane的move1方法,和move2方法,Renderer类会根据Plane的状态渲染出这个Plane,我们期望的状态是Plane会在(2,2), (3,3)上不停渲染,而不会出现在(2,3),(3,2)上,但结果是令人失望的。
这个类看似充分进行了同步?我想细心点的大家很容易就能发现其中的问题。
public class Five { private static float money = 0; private static boolean isObey = true; public static void main(String[] args) { new FiveCent().start(); isObey = false; money = 0.5f; } static class FiveCent extends Thread { @Override public void run() { while (!isObey) { Thread.yield(); } System.out.println("社会主义好 == " + money); } }}
有可能(虽然非常难以重现)程序会输出了 "社会主义好 ==0.5“ ,这说明在FiveCent运行的那个线程中看到了主线程对money的写入操作money =0.5f ,却没有看到对isObey的写入操作,isObey=false,这就是重排序问题,此时我们可以使用恰当的同步来抑制重排序...
(2)同一个锁的 unlock happened-before lock
(3)传递性,如果A happened-before B , B happened-before C,那么A happened-before C
好了,有了这三条原则已经可以解释很多多线程问题了,现在结合这3条原则来解释那个一定会的问题。
首先,Dispatcher线程先获得Plane的内部锁也就是lock,然后对x,y进行写操作,然后unlock, 这是在一个线程内发生的符合原则一。可以得出对x,y的写操作happened-beforeunlock。
然后,getY()得到Plane的内部锁lock,然后是一个对y的读操作,然后unlock。根据原则一可以得到 lock happened-before y的读操作,根据原则二得到 unlocak happened-before lock,再根据原则三也就是传递性得到,对x,y的写操作 happened-before y的读操,也就是说,x,y的写操作一对会被y的读操作正确观测到,这就解释了一定会的问题。
有了基本的概念,下篇文章会说下如何构建线程安全的类。