日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

java 锁的类型_Java锁的种类 - shawnplaying的个人页面 - OSCHINA - 中文开源技术交流社区...

發布時間:2024/4/11 java 41 豆豆
生活随笔 收集整理的這篇文章主要介紹了 java 锁的类型_Java锁的种类 - shawnplaying的个人页面 - OSCHINA - 中文开源技术交流社区... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java鎖和并發需要結合在一塊了理解,涉及到了多個話題。

本文主要參考了?http://ifeve.com/java_lock_see1/?但是我認為原文中有某些錯誤,我在下面的代碼中做了修改。

公平鎖和非公平鎖。

所謂公平鎖,就是多個線程解鎖的順序與進入鎖的順序一樣,即誰先鎖,誰就先解鎖。反之則是非公平鎖。例如ReentrantLock中就有公平與非公平兩種鎖實現,默認是非公平鎖。

public?ReentrantLock()?{

sync?=?new?NonfairSync();

}

下面討論幾種鎖:

1 自旋鎖。所謂自旋,就是在一個循環中處理。例如:

//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;

}

}

/**

*?自旋鎖,會不停地在循環中獲取值,這種方式會耗盡CPU。

*?原子類中很多方法都采用了類似的循環方法,這種方法我覺得適用于執行時間很短的操作。

*?例如在原子類中getAndDecrement等方法,都是采用了循環的方式取值,這種操作雖然用了循環,但是每次操作瞬間完成,總體上講應該不會特別耗費CPU資源。

*

*

*?總之不能讓線程中的代碼長時間在一個什么都不做的循環中,例如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);

}

}

自旋的問題在一直在執行循環,這樣會導致CPU使用率高的問題。在windows的服務器上實測,開啟50個線程(線程數量超過CPU的核數),會導致每個CPU核都達到100%的使用率,所以這就意味著這種方法不是一個可用的方法,尤其并發任務重的時候。

同樣的場景,如果使用ReentrantLock的話,CPU是很低的,所以ReentrantLock是一個很好的選擇。

當時對于SpinLock,如果在lock的循環中加入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

//并發操作的線程都將運行在這段代碼,它是非常耗費CPU資源的操作。

}

preNode?=?null;

LOCAL.set(node);

}

}

public?void?unlock()?{

CLHNode?node?=?LOCAL.get();

if?(!UPDATER.compareAndSet(this,?node,?null))?{

//這個if判斷的作用是:如果只有一個lock操作,那么if中的判斷應該為false,同時它意味著沒有并發。

//如果同時有多于一個lock操作,那么if將返回true,則執行if中的操作。同時它表示有并發。

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鎖,與之不同的是,它使得線程的狀態發生變化,因為使用了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操作,程序依然有問題將進入死循環。

在JAVA環境下 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 對于高并發,要使用可重入鎖,推薦ReentrantLock。

2 自旋鎖可以進行擴展來實現更多功能功能。比如在等待中加入其它操作。這個值得再思考。

3 以后想起再補充吧。

總結

以上是生活随笔為你收集整理的java 锁的类型_Java锁的种类 - shawnplaying的个人页面 - OSCHINA - 中文开源技术交流社区...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。