由于cpu处理速度和内存读取速度相差太远,同时cpu对内存的读取消耗要大于对缓冲的读取,所以为每个cpu加了自己的缓冲区域。从而导致了线程之间的可见性问题。
在程序执行期间,会有虚拟机编译优化和处理器编译优化。从而导致了线程之间的有序性问题。
java引入了内存模型,它描述了多线程代码中哪些行为是合法的。按照我们的要求禁用缓冲和编译优化。
其中,votilate,final,synchronized,happened-before原则 就是用来解决可见性和有序性问题。
注意,如果用俩把锁保护同一资源,一把锁修改资源,另一把锁查看资源。那么修改锁的释放,所得资源的修改并不对另一线程查看资源又可见性。
俩个线程,一个执行get,一个执行addOne,get返回的值并不一定是1.这俩个临界区不存在互斥关系。
class SafeCalc {
static long value = 0L;
synchronized long get() {
return value;
}
synchronized static void addOne() {
value += 1;
}
}
原子性怎么保证呢,java引入了锁机制,synchronized和lock
synchronized既能保证可见性,也能保证原子性。
解决原子性问题,是要保证中间状态对外不可见。
利用锁可得注意产生死锁的四个条件:互斥,占有且等待,不可抢占,循环等待
只要破坏其中一个条件即可。
互斥是我们加锁的目的,不可破坏。
破坏占有且等待,一次性申请所有资源,若申请不到则不申请
破坏不可抢占,再次申请资源时,申请不到了,则把当前占有的也释放了 Lock中提供的丰富的API,其中tryLock就是对此方式的实现。
破坏循环等待,将资源进行编号,按照一定规则进行申请。