java多線程與高并發(fā)(五)LockSupport

回顧

首先我們回顧一下前面四節(jié)所講的東西

1.線程的基本概念
2.synchronized,底層實(shí)現(xiàn)原理,鎖升級(無鎖-偏向鎖-輕量級鎖-重量級鎖)
3.volatile,線程隔離可見性,禁止指令重排序
4.AtomicXXX
5.各種UC同步框架(ReentrantLock,CountDownLatch,CyclicBarrier,Phaser,ReadWriteLock,Semaphore,Exchange)
6.synchronized 和 各種線程同步框架的ReenrantLock有何不同?
6.1.synchronizec:系統(tǒng)自帶,系統(tǒng)自動加鎖,自動解鎖,不可以出現(xiàn)多個(gè)不同的等待隊(duì)列,默認(rèn)進(jìn)行四種鎖的升級
6.2.ReentrantLock:需要自動加鎖,手動解鎖,可以出現(xiàn)多個(gè)不同的等待隊(duì)列,CAS的實(shí)現(xiàn)
本章我們補(bǔ)一個(gè)LockSupport,然后分析兩個(gè)面試題,緊接著我會教大家閱讀源碼的技巧,最后拿AQS作源碼分析

2.LockSupport

我們下面以幾個(gè)小程序案例,對LockSupport進(jìn)行詳細(xì)解釋,在以前我們要阻塞和喚醒某一個(gè)具體的線程有很多的限制,比如

因?yàn)閣ait方法需要釋放鎖,所以必須在synchronized中使用,否則會拋出異常lllegalMonitorStateException
nofity方法必須在synchronized中使用,并且應(yīng)該指定對象
synchronized,nofity,wait的對象必須一致,一個(gè)synchronized代碼塊中只能有一個(gè)線程用wait和nofity
以上諸多的使用不便,帶來了lockSupport的好處
先來看第一個(gè)小程序

package com.learn.thread.four;

import com.sun.tools.internal.ws.wsdl.document.soap.SOAPUse;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

public class TestLockSupport {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                // 使用LockSupport進(jìn)行線程堵塞
                if (i == 5) {
                    LockSupport.park();
                }
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }

        });
        thread.start();
    }
}

上述代碼看起來是比較靈活的,可以手動控制是否上鎖,通過LockSupprt.park加鎖,有了加鎖機(jī)制,LockSupport也提供了令牌解鎖機(jī)制unpark

package com.learn.thread.four;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.LockSupport;

public class TestLockSupport2 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                // 使用LockSupport進(jìn)行線程堵塞
                if (i == 5) {
                    LockSupport.park();
                }
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }

        });
        thread.start();
        // 喚醒線程t,注意這里是比park先執(zhí)行的,當(dāng)前與釋放了一個(gè)令牌,當(dāng)park拿到這個(gè)令牌后不阻塞直接放行
        LockSupport.unpark(thread);
    }
}

注意,這里是一個(gè)park對應(yīng)著一個(gè)unpark

package com.learn.thread.four;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.LockSupport;

public class TestLockSupport3 {
    public static void main(String[] args) {
        Thread thread = new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
                // 使用LockSupport進(jìn)行線程堵塞
                if (i == 5) {
                    LockSupport.park();
                }

                // 使用LockSupport進(jìn)行線程堵塞(前面只會釋放一次鎖,這里會再次阻塞)
                if (i == 8) {
                    LockSupport.park();
                }
            }
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                System.out.println(e.getMessage());
            }

        });
        thread.start();
        LockSupport.unpark(thread);
    }

}

2.1.由以上三個(gè)小程序,我們可以總結(jié)出以下幾點(diǎn)

LockSupport不需要synchronized就可以實(shí)現(xiàn)線程的阻塞和喚醒
LockSupport.unpark可以線程LockSupport.park,并且線程不會阻塞
如果一個(gè)線程處于等待狀態(tài),連續(xù)調(diào)用了兩次park方法,就會使該線程永遠(yuǎn)無法喚醒
LockSupport中的park 和unpark的實(shí)現(xiàn)原理

其實(shí)park()和unpark()的實(shí)現(xiàn)也是由Unsefa類提供的,而Unsefa類是由C和c++語言完成的,其實(shí)原理也是可以理解,它主要是通過一個(gè)變量作為標(biāo)志,變量在0-1之間來回切換,當(dāng)這個(gè)變量大于0的時(shí)候線程就獲得了“令牌”(unpark的操作),相反park的操作就是將這個(gè)變量變成0,用于識別令牌。

2.2.淘寶面試題1

實(shí)現(xiàn)一個(gè)容器,提供兩個(gè)方法add和size 寫兩個(gè)線程
線程1:添加十個(gè)元素到容器中
線程2:實(shí)時(shí)監(jiān)控元素個(gè)數(shù),當(dāng)個(gè)數(shù)到5個(gè)時(shí),線程2給出提示并結(jié)束

小程序1
我們先來分析一下小程序1的執(zhí)行流程:
通過內(nèi)部的List來new 一個(gè)ArraryList,在自定義的add方法直接調(diào)用list的add方法,在自定義的size方法調(diào)用list的size方法。然后小程序初始化這個(gè)List,啟動線程t1來做一個(gè)循環(huán),每次循環(huán)都添加一個(gè)對象,加一個(gè)打印,然后間隔一秒,在t2線程寫一個(gè)while循環(huán),實(shí)時(shí)監(jiān)控集合中對象的變換,如果數(shù)量達(dá)到5就結(jié)束t2的線程。

package com.learn.thread.four.list;

import java.util.ArrayList;
import java.util.List;

public class TestListAdd1 {
    List<Object> list = new ArrayList<Object>(16);

    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }

    public static void main(String[] args) {
        TestListAdd1 testListAdd1 = new TestListAdd1();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                testListAdd1.add(new Object());
                System.out.println(i);
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();
        // t2 線程監(jiān)聽集合的數(shù)量,發(fā)現(xiàn)獲取不到數(shù)量為5的時(shí)候,原因是線程之間不可見的原因,
        // 因?yàn)閠1 添加完對象之后,肯定會更新size 方法,但是在更新size的過程中,t2線程已經(jīng)開始讀了size方法,造成了數(shù)據(jù)不一致
        new Thread(() -> {
            while (true) {
                if (testListAdd1.size() == 5 ) {
                    break;
                }
            }
            System.out.println("t2 結(jié)束");
        },"t2").start();

    }
}

結(jié)論:
方法并沒有按預(yù)期的執(zhí)行,這是因?yàn)?,ArraryList的Size方法肯定要更新的,但是還沒有更新完,線程2就讀了,所以這時(shí)候永遠(yuǎn)不會檢測到List的長度為5了,原因就是線程之間的不可見因素

小程序2

package com.learn.thread.four.list;

import java.util.ArrayList;
import java.util.List;

public class TestListAdd2 {
    // 這里用volatile修飾引用類型,發(fā)現(xiàn)并沒有做到線程的之間的可見性,因?yàn)橐脤ο笾赶虻氖且粋€(gè)地址,
    // 如果這個(gè)對象的內(nèi)部值被改變了,是無法被觀察到的
    volatile List<Object> list = new ArrayList<Object>(16);

    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }

    public static void main(String[] args) {
        TestListAdd2 testListAdd2 = new TestListAdd2();
        new Thread(() -> {
            for (int i = 0; i < 10; i++) {
                testListAdd2.add(new Object());
                System.out.println(i);
            }
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();
        // t2 線程監(jiān)聽集合的數(shù)量,發(fā)現(xiàn)獲取不到數(shù)量為5的時(shí)候,原因是線程之間不可見的原因,
        new Thread(() -> {
            while (true) {
                if (testListAdd2.size() == 5 ) {
                    break;
                }
            }
            System.out.println("t2 結(jié)束");
        },"t2").start();

    }
}

結(jié)論:
小程序2在小程序1的基礎(chǔ)上加上了線程可見volitate修飾,但是還是無法滿足需求,這是因?yàn)関olitate要去修飾普通的值,不要去修飾引用值,因?yàn)樾揎椧妙愋停@個(gè)引用對象指向的是另外一個(gè)new出來的對象,如果這個(gè)對象里邊的成員對象改變了,是無法被觀察到的。
下面自行寫了個(gè)測試修飾引用類型的demo

package com.learn.thread.four.list;

import com.learn.thread.first.T;

public class Test {
    private volatile static int a = 0;
    // private volatile static Integer a = 0;

    public void test() {
        a++;
        System.out.println(a);
    }

    public static void main(String[] args) {
        Thread[] threads = new Thread[100];
        Test test = new Test();
        for (int i = threads.length - 1; i >= 0; i--) {
            threads[i] = new Thread(() -> test.test());
            threads[i].start();
        }

    }
}

小程序3

package com.learn.thread.four.list;

import java.util.ArrayList;
import java.util.List;

/**
 * 用對象鎖
 * wait,和nofity實(shí)現(xiàn)
 */
public class TestListAdd3 {
    private List<Object> list = new ArrayList<Object>(16);

    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }

    public static void main(String[] args) {
        TestListAdd3 testListAdd3 = new TestListAdd3();
        final Object lock = new Object();
        // t2 要早于t1啟動
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("t2 啟動");
                if (testListAdd3.size() != 5 ) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 結(jié)束");
            }
        },"t2").start();
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("t1 啟動");
                for (int i = 0; i < 10; i++) {
                    testListAdd3.add(new Object());
                    System.out.println(i);
                    if (i == 5) {
                        // 此處有坑
                        // 特別注意,nofity并不釋放當(dāng)前鎖,t1 會繼續(xù)往下走,等t1 執(zhí)行完了, t2 才會拿到這把對象鎖
                        lock.notify();
                    }
                }
                try {
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
    }
}

結(jié)論:
小程序3 也是行不通,原因是nofity方法不釋放鎖,當(dāng)t1線程調(diào)用nofity方法,并沒有釋放當(dāng)前鎖,所以t1還是會繼續(xù)運(yùn)行,等待t1執(zhí)行完畢,t2才會繼續(xù)執(zhí)行,這個(gè)時(shí)候當(dāng)前List不只有5個(gè)了。

小程序4

package com.learn.thread.four.list;

import java.util.ArrayList;
import java.util.List;

public class TestListAdd4 {
    private List<Object> list = new ArrayList<Object>(16);

    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }

    public static void main(String[] args) {
        TestListAdd4 testListAdd4 = new TestListAdd4();
        final Object lock = new Object();
        // t2 要早于t1啟動
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("t2 啟動");
                if (testListAdd4.size() != 5 ) {
                    try {
                        lock.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println("t2 結(jié)束");
                // 釋放當(dāng)前鎖,喚醒t1
                lock.notify();
            }
        },"t2").start();
        new Thread(() -> {
            synchronized (lock) {
                System.out.println("t1 啟動");
                for (int i = 0; i < 10; i++) {
                    testListAdd4.add(new Object());
                    System.out.println(i);
                    if (i == 5) {
                        // 此處有坑
                        // 特別注意,nofity并不釋放當(dāng)前鎖,t1 會繼續(xù)往下走,等t1 執(zhí)行完了, t2 才會拿到這把對象鎖
                        // 所以下面加多一個(gè)操作,讓此線程等待,阻塞此線程,讓t2 去執(zhí)行
                        lock.notify();
                        try {
                            lock.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                }
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1").start();
    }
}

結(jié)論
小程序4基本上滿足的需求,在小程序3的基礎(chǔ)上了,nofity之后讓本線程等待,讓給t2執(zhí)行,然后t2執(zhí)行完畢,釋放nofity,回到t1執(zhí)行。

小程序5

package com.learn.thread.four.list;


import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

/**
 * 這里使用CountDown實(shí)現(xiàn)
 */
public class TestListAdd5 {
    private List<Object> list = new ArrayList<Object>(16);

    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }

    public static void main(String[] args) {
        TestListAdd5 testListAdd5 = new TestListAdd5();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        new Thread(() -> {
            System.out.println("t2 啟動");
            if (testListAdd5.size() != 5) {
                try {
                    countDownLatch.await();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            System.out.println("t2 結(jié)束");
        }, "t2").start();
        new Thread(() -> {
            System.out.println("t1 啟動");
            for (int i =0; i < 10; i++) {
                testListAdd5.add(new Object());
                System.out.println(i);
                if (testListAdd5.size() == 5) {
                    // 暫停t1 線程, 打開門閥
                    countDownLatch.countDown();
                }
                // 如果這段代碼去除了,就不會給t2時(shí)間去執(zhí)行,所以這里需要等待一會
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }, "t1 ").start();
    }
}

結(jié)論:
從執(zhí)行結(jié)果來看,并沒有多大的問題,但是如果我們把休眠一秒的代碼去掉,會發(fā)現(xiàn),執(zhí)行結(jié)果不正確,這事因?yàn)閠1線程對象增加到5個(gè)時(shí),t2的線程門閥確實(shí)被打開了,但是t1線程馬上又會接著執(zhí)行,t1之前是休眠1秒,給t2線程執(zhí)行時(shí)間,如果注釋掉這段代碼,t2就沒有機(jī)會去實(shí)時(shí)監(jiān)控了。

小程序6

package com.learn.thread.four.list;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;

public class TestListAdd6 {
    private List<Object> list = new ArrayList<Object>(16);

    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }

    public static void main(String[] args) throws InterruptedException {
        TestListAdd6 testListAdd6 = new TestListAdd6();
        CountDownLatch countDownLatch = new CountDownLatch(1);
        new Thread(() -> {
            System.out.println("t2 啟動");
            if (testListAdd6.size() != 5) {
                try {
                    countDownLatch.await();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            System.out.println("t2 結(jié)束");
        }, "t2").start();

        new Thread(() -> {
            System.out.println("t1 啟動");
            for (int i =0; i < 10; i++) {
                testListAdd6.add(new Object());
                System.out.println(i);
                if (testListAdd6.size() == 5) {
                    //  打開門閥
                    countDownLatch.countDown();
                    // 等待
                    try {
                        countDownLatch.await();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    try {
                        Thread.sleep(1000L);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

            }
        }, "t1").start();
    }
}

結(jié)論
這段代碼很好理解,就是在t1線程打開門閥的時(shí)候,給自己再加一個(gè)門閥,但是這個(gè)線程睡眠代碼,還是不能去除。

小程序7

package com.learn.thread.four.list;

import com.learn.thread.first.T;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.locks.LockSupport;

public class TestListAdd7 {
    private List<Object> list = new ArrayList<Object>(16);
    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }
    static Thread t2 = null;
    static Thread t1 = null;
    public static void main(String[] args) {
        TestListAdd7 testListAdd7 = new TestListAdd7();
        t1 = new Thread(() -> {
            System.out.println("t1 啟動了");
            for (int i =0; i < 10; i++) {
                testListAdd7.add(new Object());
                System.out.println(i);
                if (testListAdd7.size() == 5) {
                    LockSupport.unpark(t2);
                    LockSupport.park();
                }

            }
        },"t1");
        t2 = new Thread(() -> {
            System.out.println("t2 啟動了");
            if (testListAdd7.size() != 5) {
                LockSupport.park();
            }
            System.out.println("t2 結(jié)束");
            LockSupport.unpark(t1);
        },"t2");
        t2.start();
        t1.start();

    }
}

結(jié)論:
這次跟之前也是大同小異,只不過鎖的方式不一樣罷了

小程序8

package com.learn.thread.four.list;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class TestListAdd9 {
    private List<Object> list = new ArrayList<Object>(16);
    public void add(Object object) {
        this.list.add(object);
    }
    public int size() {
        return this.list.size();
    }
    static Thread t1,t2 = null;
    public static void main(String[] args) {
        TestListAdd8 testListAdd8 = new TestListAdd8();
        Semaphore semaphore = new Semaphore(1);
        t1 = new Thread(() -> {
            // 保證只有一個(gè)線程執(zhí)行
            try {
                semaphore.acquire();
                for (int i = 0; i < 5; i++) {
                    testListAdd8.add(new Object());
                    System.out.println(i);
                }
                // 釋放當(dāng)前線程,讓別的線程可以加入
                semaphore.release();
                t2.start();
                t2.join();
                // 此線程繼續(xù)執(zhí)行
                semaphore.acquire();
                for (int i = 5; i < 10; i++) {
                    testListAdd8.add(new Object());
                    System.out.println(i);
                }
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t2 = new Thread(() -> {
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("t2 結(jié)束");
            semaphore.release();
        });
        t1.start();
    }
}

結(jié)論
本次是一次牽強(qiáng)的方法,用Semaphre限制每次只有一個(gè)線程執(zhí)行,當(dāng)t1增加到4的時(shí)候,釋放當(dāng)前線程,并且將cpu交給t2執(zhí)行,這時(shí)候t2也是保證只有一個(gè)線程執(zhí)行,執(zhí)行完后立馬釋放,t1調(diào)用join完之后可以繼續(xù)獲得Semaphre的鎖,然后繼續(xù)增加對象,最后釋放線程。

針對以上8個(gè)小程序,我們分別用了volitate,wait,nofity,Semphore,CountDownLatch,LockSupport,其中wait和nofity要牢牢掌握

2.3.淘寶面試題2

寫一個(gè)固定容器,擁有put和get方法,以及getCount方法,能夠支持2個(gè)生產(chǎn)者線程以及10個(gè)消費(fèi)者線程阻塞調(diào)用

小程序1

package com.learn.thread.four.prodducer;

import java.util.LinkedList;

public class TestProducer<T> {
    private final LinkedList<T> list = new LinkedList<T>();

    public final int MAX = 10;

    private int count = 0;
    
    public synchronized void put(T t) {
        // 想想這里什么用while
        // 這是因?yàn)楫?dāng)linkedList集合中的個(gè)數(shù)達(dá)到最大值得時(shí)候,if判斷了集合的大小等于MAX
        // 調(diào)用了wait方法,它不會再去判斷一次,而是繼續(xù)往下走,假如wait以后,有別的生產(chǎn)者線程添加數(shù)據(jù),那么,這里就沒有
        // 再次判斷,又添加了一次,造成了數(shù)據(jù)錯誤
        while (list.size() == MAX) {
            try {
                this.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        list.add(t);
        ++ count;
        System.out.println("生產(chǎn)者生產(chǎn) " + count);
        // 喚醒所有在隊(duì)列中等待的線程
        this.notifyAll();
    }

    public synchronized T get() {
        T t = null;
        while (list.size() == 0) {
            try {
                this.wait();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        System.out.println(Thread.currentThread().getName() + "消費(fèi)");
        t = list.removeFirst();
        count --;
        System.out.println(count);
        this.notifyAll();
        return t;
    }

    public static void main(String[] args) {
        TestProducer<String> testProducer = new TestProducer<>();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println(testProducer.get());
                }
            }, "c" + i).start();

        }
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {
                    testProducer.put(Thread.currentThread().getName() + "" + j);
                }
            }, "p" + i).start();

        }
    }
}

這里有一個(gè)很遺憾的問題,那就是nofityAlll,如果生產(chǎn)者生產(chǎn)完,是有可能喚醒一個(gè)還是生產(chǎn)者的線程,所以這里可以做一個(gè)優(yōu)化

小程序2

package com.learn.thread.four.prodducer;

import java.util.LinkedList;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class TestProducer2<T> {
    private final LinkedList<T> list = new LinkedList<T>();

    public final int MAX = 10;

    private int count = 0;

    private ReentrantLock lock = new ReentrantLock();

    Condition producer = lock.newCondition();

    Condition comsumer = lock.newCondition();

    public void put(T t) {
        // 想想這里什么用while
        // 這是因?yàn)楫?dāng)linkedList集合中的個(gè)數(shù)達(dá)到最大值得時(shí)候,if判斷了集合的大小等于MAX
        // 調(diào)用了wait方法,它不會再去判斷一次,而是繼續(xù)往下走,假如wait以后,有別的生產(chǎn)者線程添加數(shù)據(jù),那么,這里就沒有
        // 再次判斷,又添加了一次,造成了數(shù)據(jù)錯誤
        try {
            lock.lock();
            while (list.size() == MAX) {
                try {
                    producer.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            list.add(t);
            ++ count;
            System.out.println("生產(chǎn)者生產(chǎn) " + count);
            // 喚醒所有消費(fèi)者等待的線程
            comsumer.signalAll();
        }catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            lock.unlock();
        }

    }

    public T get() {
        T t = null;
        try {
            lock.lock();
            while (list.size() == 0) {
                try {
                    comsumer.await();
                } catch (Exception ex) {
                    ex.printStackTrace();
                }
            }
            System.out.println(Thread.currentThread().getName() + "消費(fèi)");
            t = list.removeFirst();
            count --;
            System.out.println(count);
            producer.signalAll();
        }catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            lock.unlock();
        }

        return t;
    }

    public static void main(String[] args) {
        TestProducer2<String> testProducer = new TestProducer2<>();
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    System.out.println(testProducer.get());
                }
            }, "c" + i).start();

        }
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                for (int j = 0; j < 25; j++) {
                    testProducer.put(Thread.currentThread().getName() + "" + j);
                }
            }, "p" + i).start();

        }
    }
}

這里ReentrantLock的優(yōu)勢就體現(xiàn)出來了,它可以有多種情況Condition,在put方法了達(dá)到了峰值就是生產(chǎn)者producer.await,反之就是comsumer.await
ReentrantLock的本質(zhì)就是synchronzied里調(diào)用wait和nofity的時(shí)候,它只有一個(gè)等待隊(duì)列,但是用了Condition就是多個(gè)等待隊(duì)列。當(dāng)我們使用producer.await的時(shí)候,就是進(jìn)入了producer的等待隊(duì)列,producer.signalAll指的是喚醒producer這個(gè)等待隊(duì)列的線程。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容