国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Java > 正文

JAVA中的鎖機(jī)制

2019-11-06 06:40:49
字體:
供稿:網(wǎng)友

鎖作為并發(fā)共享數(shù)據(jù),保證一致性的工具,在java平臺(tái)有多種實(shí)現(xiàn)(如 synchronized 和 ReentrantLock等等 ) 。java平臺(tái)下的鎖如下所示:

1、自旋鎖2、自旋鎖的其他種類3、阻塞鎖4、可重入鎖5、讀寫鎖6、互斥鎖7、悲觀鎖8、樂觀鎖9、公平鎖10、非公平鎖11、偏向鎖12、對(duì)象鎖13、線程鎖14、鎖粗化15、輕量級(jí)鎖16、鎖消除17、鎖膨脹18、信號(hào)量

1、自旋鎖

自旋鎖是采用讓當(dāng)前線程不停地的在循環(huán)體內(nèi)執(zhí)行實(shí)現(xiàn)的,當(dāng)循環(huán)的條件被其他線程改變時(shí) 才能進(jìn)入臨界區(qū)。如下

public class SpinLock {  PRivate AtomicReference<Thread> 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);  }}

使用了CAS原子操作,lock函數(shù)將owner設(shè)置為當(dāng)前線程,并且預(yù)測(cè)原來的值為空。unlock函數(shù)將owner設(shè)置為null,并且預(yù)測(cè)值為當(dāng)前線程。

當(dāng)有第二個(gè)線程調(diào)用lock操作時(shí)由于owner值不為空,導(dǎo)致循環(huán)一直被執(zhí)行,直至第一個(gè)線程調(diào)用unlock函數(shù)將owner設(shè)置為null,第二個(gè)線程才能進(jìn)入臨界區(qū)。

由于自旋鎖只是將當(dāng)前線程不停地執(zhí)行循環(huán)體,不進(jìn)行線程狀態(tài)的改變,所以響應(yīng)速度更快。但當(dāng)線程數(shù)不停增加時(shí),性能下降明顯,因?yàn)槊總€(gè)線程都需要執(zhí)行,占用CPU時(shí)間。如果線程競(jìng)爭(zhēng)不激烈,并且保持鎖的時(shí)間段。適合使用自旋鎖。

注:該例子為非公平鎖,獲得鎖的先后順序,不會(huì)按照進(jìn)入lock的先后順序進(jìn)行。

2.自旋鎖的其他種類

在自旋鎖中 另有三種常見的鎖形式:TicketLock ,CLHlock 和MCSlock

Ticket鎖主要解決的是訪問順序的問題,主要的問題是在多核cpu上

import java.util.concurrent.atomic.AtomicInteger;public class TicketLock {    private AtomicInteger serviceNum = new AtomicInteger();    private AtomicInteger ticketNum = new AtomicInteger();    private static final ThreadLocal<Integer> LOCAL= new ThreadLocal<Integer>();    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);    }}每次都要查詢一個(gè)serviceNum 服務(wù)號(hào),影響性能(必須要到主內(nèi)存讀取,并阻止其他cpu修改)。

CLHLock 和MCSLock 則是兩種類型相似的公平鎖,采用鏈表的形式進(jìn)行排序,

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<CLHNode>  LOCAL   = new ThreadLocal<CLHNode>();    private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> 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) {            while (preNode.isLocked) {            }            preNode = null;            LOCAL.set(node);        }    }    public void unlock() {        CLHNode node = LOCAL.get();        if (!UPDATER.compareAndSet(this, node, null)) {            node.isLocked = false;        }        node = null;    }}

CLHlock是不停的查詢前驅(qū)變量, 導(dǎo)致不適合在NUMA 架構(gòu)下使用(在這種結(jié)構(gòu)下,每個(gè)線程分布在不同的物理內(nèi)存區(qū)域)

MCSLock則是對(duì)本地變量的節(jié)點(diǎn)進(jìn)行循環(huán)。不存在CLHlock 的問題。

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;public class MCSLock {    public static class MCSNode {        volatile MCSNode next;        volatile boolean isLocked = true;    }private static final ThreadLocal<MCSNode> NODE = new ThreadLocal<MCSNode>();    @SuppressWarnings("unused")    private volatile MCSNode queue;    private static final AtomicReferenceFieldUpdater<MCSLock, MCSNode> 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)) {            } else {                while (currentNode.next == null) {                }            }        } else {            currentNode.next.isLocked = false;            currentNode.next = null;        }    }}

從代碼上 看,CLH 要比 MCS 更簡(jiǎn)單,

CLH 的隊(duì)列是隱式的隊(duì)列,沒有真實(shí)的后繼結(jié)點(diǎn)屬性。

MCS 的隊(duì)列是顯式的隊(duì)列,有真實(shí)的后繼結(jié)點(diǎn)屬性。

JUC ReentrantLock 默認(rèn)內(nèi)部使用的鎖 即是 CLH鎖(有很多改進(jìn)的地方,將自旋鎖換成了阻塞鎖等等)。

三、阻塞鎖:

阻塞鎖,與自旋鎖不同,改變了線程的運(yùn)行狀態(tài)。在JAVA環(huán)境中,線程Thread有如下幾個(gè)狀態(tài):

1,新建狀態(tài)

2,就緒狀態(tài)

3,運(yùn)行狀態(tài)

4,阻塞狀態(tài)

5,死亡狀態(tài)

阻塞鎖,可以說是讓線程進(jìn)入阻塞狀態(tài)進(jìn)行等待,當(dāng)獲得相應(yīng)的信號(hào)(喚醒,時(shí)間) 時(shí),才可以進(jìn)入線程的準(zhǔn)備就緒狀態(tài),準(zhǔn)備就緒狀態(tài)的所有線程,通過競(jìng)爭(zhēng),進(jìn)入運(yùn)行狀態(tài)。JAVA中,能夠進(jìn)入/退出、阻塞狀態(tài)或包含阻塞鎖的方法有 ,synchronized 關(guān)鍵字(其中的重量鎖),ReentrantLock,Object.wait()/notify(),LockSupport.park()/unpart()(j.u.c經(jīng)常使用)

下面是一個(gè)JAVA 阻塞鎖實(shí)例

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<CLHNode> LOCAL   = new ThreadLocal<CLHNode>();    private static final AtomicReferenceFieldUpdater<CLHLock1, CLHNode> 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(this);            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);        }        node = null;    }}

在這里我們使用了LockSupport.unpark()的阻塞鎖。 該例子是將CLH鎖修改而成。

阻塞鎖的優(yōu)勢(shì)在于,阻塞的線程不會(huì)占用cpu時(shí)間, 不會(huì)導(dǎo)致 CPu占用率過高,但進(jìn)入時(shí)間以及恢復(fù)時(shí)間都要比自旋鎖略慢。

在競(jìng)爭(zhēng)激烈的情況下 阻塞鎖的性能要明顯高于 自旋鎖。

理想的情況則是; 在線程競(jìng)爭(zhēng)不激烈的情況下,使用自旋鎖,競(jìng)爭(zhēng)激烈的情況下使用,阻塞鎖。

四、可重入鎖:

本文里面講的是廣義上的可重入鎖,而不是單指JAVA下的ReentrantLock。

可重入鎖,也叫做遞歸鎖,指的是同一線程 外層函數(shù)獲得鎖之后 ,內(nèi)層遞歸函數(shù)仍然有獲取該鎖的代碼,但不受影響。在JAVA環(huán)境下 ReentrantLock 和synchronized 都是 可重入鎖

下面是使用實(shí)例

public class Test implements Runnable{    public synchronized void get(){        System.out.println(Thread.currentThread().getId());        set();    }    public synchronized void set(){        System.out.println(Thread.currentThread().getId());    }    @Override    public void run() {       get();    }    public static void main(String[] args) {        Test ss=new Test();        new Thread(ss).start();        new Thread(ss).start();        new Thread(ss).start();    }}

public class Test implements Runnable {    ReentrantLock lock = new ReentrantLock();    public void get() {        lock.lock();        System.out.println(Thread.currentThread().getId());        set();        lock.unlock();    }    public void set() {        lock.lock();        System.out.println(Thread.currentThread().getId());        lock.unlock();    }    @Override    public void run() {        get();    }    public static void main(String[] args) {        Test ss = new Test();        new Thread(ss).start();        new Thread(ss).start();        new Thread(ss).start();    }}

 
兩個(gè)例子最后的結(jié)果都是正確的,即 同一個(gè)線程id被連續(xù)輸出兩次。結(jié)果如下:Threadid: 8Threadid: 8Threadid: 10Threadid: 10Threadid: 9Threadid: 9可重入鎖最大的作用是避免死鎖我們以自旋鎖作為例子,
public class SpinLock {private AtomicReference<Thread> owner =new AtomicReference<>();	public void lock(){		Thread current = Thread.currentThread();		while(!owner.compareAndSet(null, current)){		}	}	public void unlock (){		Thread current = Thread.currentThread();		owner.compareAndSet(current, null);	}}對(duì)于自旋鎖來說,1、若有同一線程兩調(diào)用lock() ,會(huì)導(dǎo)致第二次調(diào)用lock位置進(jìn)行自旋,產(chǎn)生了死鎖說明這個(gè)鎖并不是可重入的。(在lock函數(shù)內(nèi),應(yīng)驗(yàn)證線程是否為已經(jīng)獲得鎖的線程)2、若1問題已經(jīng)解決,當(dāng)unlock()第一次調(diào)用時(shí),就已經(jīng)將鎖釋放了。實(shí)際上不應(yīng)釋放鎖。(采用計(jì)數(shù)次進(jìn)行統(tǒng)計(jì))修改之后,如下:
public class SpinLock1 {	private AtomicReference<Thread> 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);		}	}}

該自旋鎖即為可重入鎖。


上一篇:Java解決八皇后問題

下一篇:Java--File類

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 太康县| 蚌埠市| 卢氏县| 梁山县| 上蔡县| 黄陵县| 田东县| 扶余县| 延长县| 微博| 万安县| 隆安县| 治县。| 大关县| 平凉市| 玉溪市| 日照市| 宽甸| 托克逊县| 卢龙县| 新干县| 内黄县| 盘锦市| 鄂州市| 灵台县| 积石山| 宝山区| 利津县| 唐河县| 林口县| 秭归县| 长子县| 张北县| 水富县| 赣榆县| 扎囊县| 军事| 南通市| 彰化市| 喀喇沁旗| 富宁县|