并发编程原则:
1. 原子性:一个操作从开始到结束不被中断,知道执行结束。
2. 可见性:Java提供 volatile [ˈvɑːlətl] 关键字保证可见性。当一个共享变量被volatile修饰时,会让被改变的值立刻被更新到内存。当其他线程访问该变量时,会读取到更新后的值。
通过 Synchronized 和 lock 也可以保证可见性。这两个关键字可以保证共享变量在同一时刻只被当前线程访问,释放锁之后,会将变量的新值更新到内存中。这是其他的线程访问读取到时更新后的值。
3. 有序性:有序性体现在线程间和线程内两种场景中;
从线程本身的角度去看,方法的执行,按照“串行”方式执行。
把线程看成独立个体,多个线程并发执行非同步代码块时(没有加锁或这被volalite),可以交叉执行。执行过程中,对于共享变量的访问,修改产生的新数据不会实时的进
行更新。
来一个不恰当的比喻:人类是类。
第一种场景:小明(人类的实例化)有一个吃饭的功能(一条执行路径)。从开始吃饭(request)->入口(controller)->经过食道(传输数据)->到达胃里(service)->流经大小肠(dao,或者其他分层)->最后拉出来(进入数据库)。
第二种场景:小明,小红,Lily,Jack一块吃饭(四条执行路径)。吃着同样的饭,但是站他们每个人自己的角度去看别人。四个人吃饭的速度不一样,有的吃的慢,可能馒头刚到嘴里,有的吃的早,可能馒头正在食道向胃里流动,有的吃的快,已经到大肠了。四个人都在做同一件事情吃饭,但是因为开饭先后,执行速度,在同一时刻,他们吃的食物到达了人类身体的不同位置。
(以上的例子没有,锁机制;且往后看)
还是吃饭。
接着第二场景,小明,小红,lily,jack四位国际友人吃完饭,该上厕所了。这时候有个公共厕所,理论上一次只允许两个人进去使用。厕所计数器按开门的次数统计每天人流量,从而计算厕所的吞吐量。但是厕所没有锁,四位小朋友一次进去了,计数器只累加了一次,出现了统计错误。每个人上厕所结束后不能正确的计入统计结果内。
这时候,如果加了锁就可以解决计数器数据统计的不一致性。
解决办法:厕所门加个红绿开关。
当一个人进去后,锁门,占有锁,开关为红(占有锁)。完事后,走出厕所,计数器+1,开关为绿(释放锁)。这样就实现了统计数据的一致性!
我去,憋不住了,太有才了!
-------Volatile-------
计算机中有CPU 内存 缓存 的概念。
CPU运行时,默认查找缓存中的数据;CPU被中断,根据CPU对操作系统的管理特性,可能清空缓存,重新读取内存中的数据到缓存中;也可能不清空缓存,后续计算仍然使用缓存中数据。
Volatile关键字不改变缓存中数据,只改变内存中的数据。
当被volatile修饰的时候,相等于告知底层OS操作系统,让CPU进行计算时,去内存中读取数据,或者数据改变时,让CPU把最新数据更新到内存。
Volatile主要是为了保证内存的可见性。(这是抄的,不太理解这么书面的语言。我的理解是,volatile中文意思是不稳定,反复无常的。有Volatile标识的变量,表示该变量数据更新频繁,数据状态变化多。意思是,告诉CPU,这个变量数据变化频繁,如果要使用,需要读取CPU,获取最新的数据)