多线程--内存可见性&&原子性

早上突然想起多线程的内存可见性跟原子性!趁着脑子发热,马上写写!

说起内存可见性跟原子性,不可避免会想起几个关键字synchronized、volatile、AtomInteger;想起几个名词 指令重排序、as-if-serial语义、java八个指令。下面就详细讲解一下!

内存可见性:这是一种jmm内存模型的定义。单线程情况下,所有的变量都是共享变量,内存都是可见的;但是如果使用多线程,那么就需要再各个内存中不断地刷新共享变量,

jmm下分为主内存(之前说了,“共享内存”)、工作内存。工作内存中要保留一份主内存中共享变量的副本,要保证线程之间的内存可见性也就是说每个工作内存中的变量如果发生变化,要同步到其他内存中(包括主内存跟工作内存),但是工作内存之间又不能相互通信(可以想一下为什么要有工作内存、主内存),那么就得分成两步:
1、工作内存中的变量刷新到主内存;
2、主内存共享变量将变量刷新到其他工作内存当中;

原子性:这个可以结合指令重排序跟as-if-serial语义来说。为了提高效率,cpu执行不会按照程序代码执行,比如:
        int a = 1;
        int b = 2;
        c = a + b;
指令重排序就是说cpu执行权可能执行顺序是先b再a,然后计算c;as-if-serial语义就是说不管你之前怎么变,最后要执行的一定是c = a + b;保证正确;

也就是说,代码不可在分割;比如 : num++;,这个就不是原子操作,因为他可以分解为mnumber = num + 1; num = mnumber;

为了保证内存可见性跟原子性,可以使用synchronized、volatile、AtomInteger、还有java6后新增的reentlock;

volatile:可以保证可见性,也就是他会及时更新所有内存中被声明为volatile的变量,而且它不会被阻塞,所以效率上来说他比synchronized(会被阻塞)要高,

但是却保证不了原子性,就像刚刚说的 num++;有可能线程获取到的值为没有进行num++的值就直接进行了下一次的++。所以一般不对这个进行计数器的操作,原则上可以对boolean
变量、温度变化这类的场景使用,而且由于不会被阻塞,所以它比synchronized效率更好!

AtomInteger:java对于++、--等操作不是线程安全的,那么对于计数器等场景的使用,我们可以采用同步代码块的方式,同时也可以使用java提供的AtomInteger,这个保证了线程安全,而且使用方便。

synchronized&&reentlock:常见的同步方式,推荐使用reetlock;这样可以保证释放掉锁,但是要注意,所有的 不管是正常或者异常的地方都要释放掉锁,所以最好是try-catch之后一定使用finally进行释放,这样就不会导致资源被占用了
posted @ 2017-02-27 11:26  kevinfuture  阅读(425)  评论(0编辑  收藏  举报