成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

Java多線程之內(nèi)置鎖與顯式鎖

hersion / 2600人閱讀

摘要:中具有通過(guò)實(shí)現(xiàn)的內(nèi)置鎖,和實(shí)現(xiàn)的顯示鎖,這兩種鎖各有各的好處,算是互有補(bǔ)充,今天就來(lái)做一個(gè)總結(jié)。內(nèi)置鎖獲得鎖和釋放鎖是隱式的,進(jìn)入修飾的代碼就獲得鎖,走出相應(yīng)的代碼就釋放鎖。是顯示鎖,需要顯示進(jìn)行以及操作。

Java中具有通過(guò)Synchronized實(shí)現(xiàn)的內(nèi)置鎖,和ReentrantLock實(shí)現(xiàn)的顯示鎖,這兩種鎖各有各的好處,算是互有補(bǔ)充,今天就來(lái)做一個(gè)總結(jié)。

Synchronized

內(nèi)置鎖獲得鎖和釋放鎖是隱式的,進(jìn)入synchronized修飾的代碼就獲得鎖,走出相應(yīng)的代碼就釋放鎖。

synchronized(list){ //獲得鎖
    list.append();
    list.count();
}//釋放鎖
通信

與Synchronized配套使用的通信方法通常有wait(),notify()。

wait()方法會(huì)立即釋放當(dāng)前鎖,并進(jìn)入等待狀態(tài),等待到相應(yīng)的notify并重新獲得鎖過(guò)后才能繼續(xù)執(zhí)行;notify()不會(huì)立刻立刻釋放鎖,必須要等notify()所在線程執(zhí)行完synchronized塊中的所有代碼才會(huì)釋放。用如下代碼來(lái)進(jìn)行驗(yàn)證:

public static void main(String[] args){
    List list = new LinkedList();
    Thread r = new Thread(new ReadList(list));
    Thread w = new Thread(new WriteList(list));
    r.start();
    w.start();
}
class ReadList implements Runnable{

    private List list;

    public ReadList(List list){ this.list = list; }

    @Override
    public void run(){
        System.out.println("ReadList begin at "+System.currentTimeMillis());
        synchronized (list){
            try {
                Thread.sleep(1000);
                System.out.println("list.wait() begin at "+System.currentTimeMillis());
                list.wait();
                System.out.println("list.wait() end at "+System.currentTimeMillis());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.println("ReadList end at "+System.currentTimeMillis());

    }
}

class WriteList implements Runnable{

    private List list;

    public WriteList(List list){ this.list = list; }

    @Override
    public void run(){
        System.out.println("WriteList begin at "+System.currentTimeMillis());
        synchronized (list){
            System.out.println("get lock at "+System.currentTimeMillis());
            list.notify();
            System.out.println("list.notify() at "+System.currentTimeMillis());
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("get out of block at "+System.currentTimeMillis());
        }
        System.out.println("WriteList end at "+System.currentTimeMillis());

    }
}

運(yùn)行結(jié)果

ReadList begin at 1493650526582
WriteList begin at 1493650526582
list.wait() begin at 1493650527584
get lock at 1493650527584
list.notify() at 1493650527584
get out of block at 1493650529584
WriteList end at 1493650529584
list.wait() end at 1493650529584
ReadList end at 1493650529584

可見讀線程開始運(yùn)行,開始wait過(guò)后,寫線程才獲得鎖;寫線程走出同步塊而不是notify過(guò)后,讀線程才wait結(jié)束,亦即獲得鎖。所以notify不會(huì)釋放鎖,wait會(huì)釋放鎖。
值得一提的是,notifyall()會(huì)通知等待隊(duì)列中的所有線程。

編碼

編碼模式比較簡(jiǎn)單,單一,不必顯示的獲得鎖,釋放鎖,能降低因粗心忘記釋放鎖的錯(cuò)誤。使用模式如下:

synchronized(object){ 
    
}
靈活性

內(nèi)置鎖在進(jìn)入同步塊時(shí),采取的是無(wú)限等待的策略,一旦開始等待,就既不能中斷也不能取消,容易產(chǎn)生饑餓與死鎖的問(wèn)題

在線程調(diào)用notify方法時(shí),會(huì)隨機(jī)選擇相應(yīng)對(duì)象的等待隊(duì)列的一個(gè)線程將其喚醒,而不是按照FIFO的方式,如果有強(qiáng)烈的公平性要求,比如FIFO就無(wú)法滿足

性能

Synchronized在JDK1.5及之前性能(主要指吞吐率)比較差,擴(kuò)展性也不如ReentrantLock。但是JDK1.6以后,修改了管理內(nèi)置鎖的算法,使得Synchronized和標(biāo)準(zhǔn)的ReentrantLock性能差別不大。

ReentrantLock

ReentrantLock是顯示鎖,需要顯示進(jìn)行 lock 以及 unlock 操作。

通信

與ReentrantLock搭配的通行方式是Condition,如下:

private Lock lock = new ReentrantLock();  
private Condition condition = lock.newCondition(); 
condition.await();//this.wait();  
condition.signal();//this.notify();  
condition.signalAll();//this.notifyAll();  

Condition是被綁定到Lock上的,必須使用lock.newCondition()才能創(chuàng)建一個(gè)Condition。從上面的代碼可以看出,Synchronized能實(shí)現(xiàn)的通信方式,Condition都可以實(shí)現(xiàn),功能類似的代碼寫在同一行中。
而Condition的優(yōu)秀之處在于它可以為多個(gè)線程間建立不同的Condition,比如對(duì)象的讀/寫Condition,隊(duì)列的空/滿Condition,在JDK源碼中的ArrayBlockingQueue中就使用了這個(gè)特性:

 public ArrayBlockingQueue(int capacity, boolean fair) {
    if (capacity <= 0)
        throw new IllegalArgumentException();
    this.items = new Object[capacity];
    lock = new ReentrantLock(fair);
    notEmpty = lock.newCondition();
    notFull =  lock.newCondition();
}
public void put(E e) throws InterruptedException {
    checkNotNull(e);
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == items.length)
            notFull.await();
        enqueue(e);
    } finally {
        lock.unlock();
    }
}
public E take() throws InterruptedException {
    final ReentrantLock lock = this.lock;
    lock.lockInterruptibly();
    try {
        while (count == 0)
            notEmpty.await();
        return dequeue();
    } finally {
        lock.unlock();
    }
}
private void enqueue(E x) {
    // assert lock.getHoldCount() == 1;
    // assert items[putIndex] == null;
    final Object[] items = this.items;
    items[putIndex] = x;
    if (++putIndex == items.length)
        putIndex = 0;
    count++;
    notEmpty.signal();
}
private E dequeue() {
    // assert lock.getHoldCount() == 1;
    // assert items[takeIndex] != null;
    final Object[] items = this.items;
    @SuppressWarnings("unchecked")
    E x = (E) items[takeIndex];
    items[takeIndex] = null;
    if (++takeIndex == items.length)
        takeIndex = 0;
    count--;
    if (itrs != null)
        itrs.elementDequeued();
    notFull.signal();
    return x;
}
編碼
Lock lock = new ReentrantLock();
lock.lock();
try{
    
}finally{
    lock.unlock();
}

相比于Synchronized要復(fù)雜一些,而且一定要記得在finally中釋放鎖而不是其他地方,這樣才能保證即使出了異常也能釋放鎖。

靈活性

lock.lockInterruptibly() 可以使得線程在等待鎖是支持響應(yīng)中斷;lock.tryLock() 可以使得線程在等待一段時(shí)間過(guò)后如果還未獲得鎖就停止等待而非一直等待。有了這兩種機(jī)制就可以更好的制定獲得鎖的重試機(jī)制,而非盲目一直等待,可以更好的避免饑餓和死鎖問(wèn)題

ReentrantLock可以成為公平鎖(非默認(rèn)的),所謂公平鎖就是鎖的等待隊(duì)列的FIFO,不過(guò)公平鎖會(huì)帶來(lái)性能消耗,如果不是必須的不建議使用。這和CPU對(duì)指令進(jìn)行重排序的理由是相似的,如果強(qiáng)行的按照代碼的書寫順序來(lái)執(zhí)行指令,就會(huì)浪費(fèi)許多時(shí)鐘周期,達(dá)不到最大利用率

性能

雖然Synchronized和標(biāo)準(zhǔn)的ReentrantLock性能差別不大,但是ReentrantLock還提供了一種非互斥的讀寫鎖,
也就是不強(qiáng)制每次最多只有一個(gè)線程能持有鎖,它會(huì)避免“讀/寫”沖突,“寫/寫”沖突,但是不會(huì)排除“讀/讀”沖突,
因?yàn)椤白x/讀”并不影響數(shù)據(jù)的完整性,所以可以多個(gè)讀線程同時(shí)持有鎖,這樣在讀寫比較高的情況下,性能會(huì)有很大的提升。

下面用兩種鎖分別實(shí)現(xiàn)的線程安全的linkedlist:

class RWLockList {//讀寫鎖

    private List list;
    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock readLock = lock.readLock();
    private final Lock writeLock = lock.writeLock();

    public RWLockList(List list){this.list = list;}

    public int get(int k) {
        readLock.lock();
        try {
            return (int)list.get(k);
        } finally {
            readLock.unlock();
        }
    }

    public void put(int value) {
        writeLock.lock();
        try {
            list.add(value);
        } finally {
            writeLock.unlock();
        }
    }
}

class SyncList  {

    private List list;

    public SyncList(List list){this.list = list;}

    public synchronized int  get(int k){
        return (int)list.get(k);
    }

    public synchronized void put(int value){
        list.add(value);
    }

}

讀寫鎖測(cè)試代碼:

List list = new LinkedList();
for (int i=0;i<10000;i++){
    list.add(i);
}
RWLockList rwLockList = new RWLockList(list);//初始化數(shù)據(jù)

Thread writer = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            rwLockList.put(i);
        }
    }
});
Thread reader1 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            rwLockList.get(i);
        }
    }
});
Thread reader2 = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            rwLockList.get(i);
        }
    }
});
long begin = System.currentTimeMillis();
writer.start();reader1.start();reader2.start();
try {
    writer.join();
    reader1.join();
    reader2.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("RWLockList take "+(System.currentTimeMillis()-begin) + "ms");

同步鎖測(cè)試代碼:

List list = new LinkedList();
for (int i=0;i<10000;i++){
    list.add(i);
}
SyncList syncList = new SyncList(list);//初始化數(shù)據(jù)
Thread writerS = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            syncList.put(i);
        }
    }
});
Thread reader1S = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            syncList.get(i);
        }
    }
});
Thread reader2S = new Thread(new Runnable() {
    @Override
    public void run() {
        for (int i=0;i<10000;i++){
            syncList.get(i);
        }
    }
});
long begin1 = System.currentTimeMillis();
writerS.start();reader1S.start();reader2S.start();
try {
    writerS.join();
    reader1S.join();
    reader2S.join();
} catch (InterruptedException e) {
    e.printStackTrace();
}
System.out.println("SyncList take "+(System.currentTimeMillis()-begin1) + "ms");

結(jié)果:

RWLockList take 248ms
RWLockList take 255ms
RWLockList take 249ms
RWLockList take 224ms

SyncList take 351ms
SyncList take 367ms
SyncList take 315ms
SyncList take 323ms

可見讀寫鎖的確是優(yōu)于純碎的互斥鎖

總結(jié)

內(nèi)置鎖最大優(yōu)點(diǎn)是簡(jiǎn)潔易用,顯示鎖最大優(yōu)點(diǎn)是功能豐富,所以能用內(nèi)置鎖就用內(nèi)置鎖,在內(nèi)置鎖功能不能滿足之時(shí)在考慮顯示鎖。

關(guān)于兩種鎖,目前接觸到的就是這么多,總結(jié)不到位之處,歡迎拍磚。我的主頁(yè) mageek.cn

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/69955.html

相關(guān)文章

  • Java鎖機(jī)制了解一下

    摘要:底層是是通過(guò)對(duì)象,對(duì)象有自己的對(duì)象頭,存儲(chǔ)了很多信息,其中一個(gè)信息標(biāo)示是被哪個(gè)線程持有。當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放。 前言 回顧前面: 多線程三分鐘就可以入個(gè)門了! Thread源碼剖析 多線程基礎(chǔ)必要知識(shí)點(diǎn)!看了學(xué)習(xí)多線程事半功倍 只有光頭才能變強(qiáng)! 本文章主要講的是Java多線程加鎖機(jī)制,有兩種: Synchronized 顯式Lock 不得不嘮...

    hyuan 評(píng)論0 收藏0
  • Java線程——重入鎖ReentrantLock源碼閱讀

    摘要:所謂的重入,就是當(dāng)本線程想再次獲得鎖,不需要重新申請(qǐng),它本身就已經(jīng)鎖了,即重入該鎖。如果不為,則表示有線程已經(jīng)占有了。總結(jié)回顧下要點(diǎn)是一個(gè)可重入的鎖被當(dāng)前占用的線程重入。 上一章《AQS源碼閱讀》講了AQS框架,這次講講它的應(yīng)用類(注意不是子類實(shí)現(xiàn),待會(huì)細(xì)講)。ReentrantLock,顧名思義重入鎖,但什么是重入,這個(gè)鎖到底是怎樣的,我們來(lái)看看類的注解說(shuō)明showImg(http:...

    sushi 評(píng)論0 收藏0
  • 從0到1實(shí)現(xiàn)自己的阻塞隊(duì)列(上)

    摘要:而且在大多數(shù)經(jīng)典的多線程編程資料中,阻塞隊(duì)列都是其中非常重要的一個(gè)實(shí)踐案例。甚至可以說(shuō)只有自己動(dòng)手實(shí)現(xiàn)了一個(gè)阻塞隊(duì)列才能真正掌握多線程相關(guān)的。為什么會(huì)發(fā)生這種情況呢原因就是在我們實(shí)現(xiàn)的這個(gè)阻塞隊(duì)列中完全沒有線程同步機(jī)制,所以同時(shí)并發(fā)進(jìn)行的個(gè) 阻塞隊(duì)列不止是一道熱門的面試題,同時(shí)也是許多并發(fā)處理模型的基礎(chǔ),比如常用的線程池類ThreadPoolExecutor內(nèi)部就使用了阻塞隊(duì)列來(lái)保存等...

    niceforbear 評(píng)論0 收藏0
  • Java鎖機(jī)制

    摘要:它倆是不沖突的,也就是說(shuō)獲取了類鎖的線程和獲取了對(duì)象鎖的線程是不沖突的可重入鎖住了鎖住了當(dāng)線程進(jìn)入到的方法時(shí),此時(shí)拿到了實(shí)例對(duì)象的鎖。當(dāng)一個(gè)線程執(zhí)行的代碼出現(xiàn)異常時(shí),其所持有的鎖會(huì)自動(dòng)釋放。 Java鎖機(jī)制 synchronized鎖 synchronized 簡(jiǎn)介 synchronized是Java的一個(gè)關(guān)鍵字,它能夠?qū)⒋a塊(方法)鎖起來(lái) synchronized是一種互...

    FreeZinG 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<