java 锁的类型_Java锁的种类 - shawnplaying的个人页面 - OSCHINA - 中文开源技术交流社区...
Java鎖和并發(fā)需要結合在一塊了理解,涉及到了多個話題。
本文主要參考了?http://ifeve.com/java_lock_see1/?但是我認為原文中有某些錯誤,我在下面的代碼中做了修改。
公平鎖和非公平鎖。
所謂公平鎖,就是多個線程解鎖的順序與進入鎖的順序一樣,即誰先鎖,誰就先解鎖。反之則是非公平鎖。例如ReentrantLock中就有公平與非公平兩種鎖實現,默認是非公平鎖。
public?ReentrantLock()?{
sync?=?new?NonfairSync();
}
下面討論幾種鎖:
1 自旋鎖。所謂自旋,就是在一個循環(huán)中處理。例如:
//AtomicInteger
public?final?int?getAndIncrement()?{
for?(;;)?{
int?current?=?get();
int?next?=?current?+?1;
if?(compareAndSet(current,?next))
return?current;
}
}
//AtomicReference
public?final?V?getAndSet(V?newValue)?{
while?(true)?{
V?x?=?get();
if?(compareAndSet(x,?newValue))
return?x;
}
}
/**
*?自旋鎖,會不停地在循環(huán)中獲取值,這種方式會耗盡CPU。
*?原子類中很多方法都采用了類似的循環(huán)方法,這種方法我覺得適用于執(zhí)行時間很短的操作。
*?例如在原子類中getAndDecrement等方法,都是采用了循環(huán)的方式取值,這種操作雖然用了循環(huán),但是每次操作瞬間完成,總體上講應該不會特別耗費CPU資源。
*
*
*?總之不能讓線程中的代碼長時間在一個什么都不做的循環(huán)中,例如while(true){},不然CPU資源會被耗盡。
*
*?@author?zhaoxp
*
*/
public?class?SpinLock?{
private?AtomicReference?sign?=new?AtomicReference<>();
public?void?lock(){
Thread?current?=?Thread.currentThread();
while(!sign?.compareAndSet(null,?current)){
}
}
public?void?unlock?(){
Thread?current?=?Thread.currentThread();
sign?.compareAndSet(current,?null);
}
}
自旋的問題在一直在執(zhí)行循環(huán),這樣會導致CPU使用率高的問題。在windows的服務器上實測,開啟50個線程(線程數量超過CPU的核數),會導致每個CPU核都達到100%的使用率,所以這就意味著這種方法不是一個可用的方法,尤其并發(fā)任務重的時候。
同樣的場景,如果使用ReentrantLock的話,CPU是很低的,所以ReentrantLock是一個很好的選擇。
當時對于SpinLock,如果在lock的循環(huán)中加入Thread.sleep(1000)的話,運行時CPU很低,所以如果沒有時效性要求,那么自旋的方式還是可以使用。
另外還有三種自旋鎖:TicketLock,CLHLock,MCSLock。
import?java.util.concurrent.atomic.AtomicInteger;
public?class?TicketLock?{
private?AtomicInteger?????????????????????serviceNum?=?new?AtomicInteger();
private?AtomicInteger?????????????????????ticketNum??=?new?AtomicInteger();
private?static?final?ThreadLocal?LOCAL??????=?new?ThreadLocal();
public?void?lock()?{
int?myticket?=?ticketNum.getAndIncrement();
LOCAL.set(myticket);
while?(myticket?!=?serviceNum.get())?{
}
}
public?void?unlock()?{
int?myticket?=?LOCAL.get();
serviceNum.compareAndSet(myticket,?myticket?+?1);
}
}
import?java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public?class?CLHLock?{
public?static?class?CLHNode?{
private?volatile?boolean?isLocked?=?true;
}
@SuppressWarnings("unused")
private?volatile?CLHNode???????????????????????????????????????????tail;
private?static?final?ThreadLocal??????????????????????????LOCAL???=?new?ThreadLocal();
private?static?final?AtomicReferenceFieldUpdater?UPDATER?=?AtomicReferenceFieldUpdater.newUpdater(CLHLock.class,CLHNode.class,?"tail");
public?void?lock()?{
CLHNode?node?=?new?CLHNode();
LOCAL.set(node);
CLHNode?preNode?=?UPDATER.getAndSet(this,?node);
if?(preNode?!=?null)?{
//preNode==null?means?it?is?first?node;
while?(preNode.isLocked)?{//?the?other?threads?all?stopped?here
//并發(fā)操作的線程都將運行在這段代碼,它是非常耗費CPU資源的操作。
}
preNode?=?null;
LOCAL.set(node);
}
}
public?void?unlock()?{
CLHNode?node?=?LOCAL.get();
if?(!UPDATER.compareAndSet(this,?node,?null))?{
//這個if判斷的作用是:如果只有一個lock操作,那么if中的判斷應該為false,同時它意味著沒有并發(fā)。
//如果同時有多于一個lock操作,那么if將返回true,則執(zhí)行if中的操作。同時它表示有并發(fā)。
node.isLocked?=?false;
}
node?=?null;
}
}
import?java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public?class?MCSLock?{
public?static?class?MCSNode?{
volatile?MCSNode?next;
volatile?boolean?isLocked?=?true;
}
private?static?final?ThreadLocal??????????????????????????NODE????=?new?ThreadLocal();
@SuppressWarnings("unused")
private?volatile?MCSNode???????????????????????????????????????????queue;
private?static?final?AtomicReferenceFieldUpdater?UPDATER?=?AtomicReferenceFieldUpdater.newUpdater(MCSLock.class,MCSNode.class,?"queue");
public?void?lock()?{
MCSNode?currentNode?=?new?MCSNode();
NODE.set(currentNode);
MCSNode?preNode?=?UPDATER.getAndSet(this,?currentNode);
if?(preNode?!=?null)?{
preNode.next?=?currentNode;
while?(currentNode.isLocked)?{
}
}
}
public?void?unlock()?{
MCSNode?currentNode?=?NODE.get();
if?(currentNode.next?==?null)?{
if?(UPDATER.compareAndSet(this,?currentNode,?null))?{
//最后一個node,并且在操作時再也沒有追加node
}?else?{
//最后一個node,但是在操作時追加node
while?(currentNode.next?==?null)?{
//等待后追加的node做preNode.next=node的操作。也就是等待后追加的node設置當前node的next?node值。
}
}
}?else?{
currentNode.isLocked?=?false;
currentNode.next?=?null;
}
}
}
這三種自旋鎖的具體實現中,雖然代碼上沒有鏈表或者隊列的數據結構,但是實際從本質上講,它們就是鏈表或者隊列的結構。通過ThreadLocal等的精巧的數據結構實現。
這里的鏈表的實現,關鍵在于:
<1> 使用了原子類中的getAndSet方法,這個實現了線程安全的得到老值,設置新值。
<2> 使用了ThreadLocal保存getAndSet中得到的老值。
2 阻塞鎖。基于上面提到的CLHLock鎖,與之不同的是,它使得線程的狀態(tài)發(fā)生變化,因為使用了LockSupport.park(this);和LockSupport.unpark(node.isLocked)的方法。
還有,我理解的synchronized語句,其實也是起到了阻塞鎖的作用。
import?java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import?java.util.concurrent.locks.LockSupport;
public?class?CLHLock1?{
public?static?class?CLHNode?{
private?volatile?Thread?isLocked;
}
@SuppressWarnings("unused")
private?volatile?CLHNode????????????????????????????????????????????tail;
private?static?final?ThreadLocal???????????????????????????LOCAL???=?new?ThreadLocal();
private?static?final?AtomicReferenceFieldUpdater?UPDATER?=?AtomicReferenceFieldUpdater.newUpdater(CLHLock1.class,CLHNode.class,?"tail");
public?void?lock()?{
CLHNode?node?=?new?CLHNode();
LOCAL.set(node);
CLHNode?preNode?=?UPDATER.getAndSet(this,?node);
if?(preNode?!=?null)?{
preNode.isLocked?=?Thread.currentThread();
LockSupport.park(preNode.isLocked);//降低CPU使用率
preNode?=?null;
LOCAL.set(node);
}
}
public?void?unlock()?{
CLHNode?node?=?LOCAL.get();
if?(!UPDATER.compareAndSet(this,?node,?null))?{
System.out.println("unlock\t"?+?node.isLocked.getName());
LockSupport.unpark(node.isLocked);//降低CPU使用率
}
node?=?null;
}
}
3 可重入鎖,也叫遞歸鎖。“指的是同一線程 外層函數獲得鎖之后 ,內層遞歸函數仍然有獲取該鎖的代碼,但不受影響。”
比如CLHLock中,如果做兩次 lock.lock()操作,即使有兩次unlock操作,程序依然有問題將進入死循環(huán)。
在JAVA環(huán)境下 ReentrantLock 和synchronized 都是 可重入鎖。
修改CLHLock,將它改進為可重入鎖:
public?class?SpinLock1?{
private?AtomicReference?owner?=new?AtomicReference<>();
private?int?count?=0;
public?void?lock(){
Thread?current?=?Thread.currentThread();
if(current==owner.get())?{
count++;
return?;
}
while(!owner.compareAndSet(null,?current)){
}
}
public?void?unlock?(){
Thread?current?=?Thread.currentThread();
if(current==owner.get()){
if(count!=0){
count--;
}else{
owner.compareAndSet(current,?null);
}
}
}
}
綜合來說,各種鎖都有其用處。
1 對于高并發(fā),要使用可重入鎖,推薦ReentrantLock。
2 自旋鎖可以進行擴展來實現更多功能功能。比如在等待中加入其它操作。這個值得再思考。
3 以后想起再補充吧。
總結
以上是生活随笔為你收集整理的java 锁的类型_Java锁的种类 - shawnplaying的个人页面 - OSCHINA - 中文开源技术交流社区...的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java groovyshell_在ja
- 下一篇: java客户端运行hadoop_JAVA