??xml version="1.0" encoding="utf-8" standalone="yes"?> 锁竞?/font> 影响锁竞争性的条g有两个:锁被h的频率和每次持有锁的旉。显然当而这二者都很小的时候,锁竞争不会成Z要的瓉。但是如果锁使用不当Q导致二者都比较大,那么很有可能CPU不能有效的处理Q务,d被大量堆U?/p>
所以减锁竞争的方式有下面三种Q?/p>
死锁 1.一U情冉|U程A永远不释NQ结果B一直拿不到锁,所以线EB?#8220;L”?br />
2.W二U情况下Q线EA拥有U程B需要的锁YQ同时线EB拥有U程A需要的锁XQ那么这时候线EA/B互相依赖Ҏ释放锁,于是二者都“L”了?br />
3.如果一个线EL不能被调度,那么{待此线E结果的U程可能死锁了。这U情况叫做线E饥饿死锁。比如说非公q锁中,如果某些U程非常z跃Q在高ƈ发情况下q类U程可能L拿到锁,那么那些z跃度低的线E可能就一直拿不到锁,q样发生了“饥饿?#8221;?/p>
避免死锁的解x案是Q?br />
1.可能的按照锁的使用规范h锁,另外锁的h_度要小Q不要在不需要锁的地方占用锁Q锁不用了尽快释放)Q?br />
2.在高U锁里面L使用tryLock或者定时机Ӟ是指定获取锁超时的旉Q如果时间到了还没有获取到锁那么放弃)。高U锁QLockQ里面的q两U方式可以有效的避免死锁?/p>
在JDK 5之前Java语言是靠synchronized关键字保证同步的Q这会导致有锁(后面的章节还会谈到锁Q?/p>
锁机制存在以下问题: Q?Q在多线E竞争下Q加锁、释N会导致比较多的上下文切换和调度gӞ引v性能问题?/p>
Q?Q一个线E持有锁会导致其它所有需要此锁的U程挂v?/p>
Q?Q如果一个优先高的U程{待一个优先低的U程释放锁会D优先U倒置Q引h能风险?/p>
volatile是不错的机制Q但是volatile不能保证原子性。因此对于同步最l还是要回到锁机制上来?/p>
独占锁是一U悲观锁Qsynchronized是一U独占锁Q会D其它所有需要锁的线E挂P{待持有锁的U程释放锁。而另一个更加有效的锁就是乐观锁。所?span style="color: red">?span style="color: red">观锁是Q?/span>
]]>
]]>
CAS 操作
上面的乐观锁用到的机制就是CASQCompare and Swap?/p>
CAS?个操作数Q内存值VQ旧的预期值AQ要修改的新值B。当且仅当预期值A和内存值V相同Ӟ内存值V修改为BQ否则什么都不做?/p>
非阻塞算?Qnonblocking algorithmsQ?/strong>
一个线E的p|或者挂起不应该影响其他U程的失败或挂v的算法?/p>
C的CPU提供了特D的指oQ可以自动更新共享数据,而且能够到其他U程的干扎ͼ?compareAndSet() qq些代替了锁定?/p>
拿出AtomicInteger来研I在没有锁的情况下是如何做到数据正确性的?/p>
private volatile int value;
首先毫无以ؓQ?span style="color: red">在没有锁的机制下可能需要借助volatile原语Q保证线E间的数据是可见的(׃n的)?/span>q样才获取变量的值的时候才能直接读取?/p>
public final int get() {
return value;
}
然后来看?+i是怎么做到的?/p>
public final int incrementAndGet() {
for (;;) {
int current = get();
int next = current + 1;
if (compareAndSet(current, next))
return next;
}
}
在这里采用了CAS操作Q每ơ从内存中读取数据然后将此数据和+1后的l果q行CAS操作Q如果成功就q回l果Q否则重试直到成功ؓ止?/p>
而compareAndSet利用JNI来完成CPU指o的操作?/p>
public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}
整体的过E就是这样子的,利用CPU的CAS指oQ同时借助JNI来完成Java的非d法。其它原子操作都是利用类似的Ҏ完成的?/p>
而整个J.U.C都是建立在CAS之上的,因此对于synchronizedd法QJ.U.C在性能上有了很大的提升?br />
CAS看v来很爽,但是会导?#8220;ABA问题”?/p>
CAS法实现一个重要前提需要取出内存中某时ȝ数据Q而在下时L较ƈ替换Q那么在q个旉差类会导致数据的变化?/p>
比如说一个线Eone从内存位|V中取出AQ这时候另一个线Etwo也从内存中取出AQƈ且twoq行了一些操作变成了BQ然后two又将V位置的数据变成AQ这时候线Eoneq行CAS操作发现内存中仍然是AQ然后one操作成功。尽线Eone的CAS操作成功Q但是不代表q个q程是没有问题的。如果链表的头在变化了两ơ后恢复了原|但是不代表链表就没有变化。因此前面提到的原子操作AtomicStampedReference/AtomicMarkableReference很有用了。这允许一对变化的元素q行原子操作?/span>
volatile保证U程间的数据是可见的Q共享的Q,但不保证数据同步
volatile相当于synchronized的弱实现Q也是说volatile实现了类似synchronized的语义,却又没有锁机制。它保对volatile字段的更C可预见的方式告知其他的线E?br />
volatile包含以下语义Q?br />
Q?QJava 存储模型不会对valatile指o的操作进行重排序Q这个保证对volatile变量的操作时按照指o的出现顺序执行的?br />
Q?Qvolatile变量不会被缓存在寄存器中Q只有拥有线E可见)或者其他对CPU不可见的地方Q每ơL从主存中dvolatile变量的结果。也是说对于volatile变量的修改,其它U程L可见的,q且不是使用自己U程栈内部的变量。也是在happens-before法则中,对一个valatile变量的写操作后,其后的Q何读操作理解可见此写操作的结果?br />
volatile变量的特性不错,但是volatileq不能保证线E安全的Q也是说volatile字段的操作不是原子性的Qvolatile变量只能保证可见性(一个线E修改后其它U程能够理解看到此变化后的结果)Q要想保证原子性,目前为止只能加锁Q?br />
volatile通常在下面的场景Q?/p>
应用volatile变量的三个原则:
Q?Q写入变量不依赖此变量的|或者只有一个线E修Ҏ变量
Q?Q变量的状态不需要与其它变量共同参与不变U束
Q?Q访问变量不需要加?/p>