Java并發(fā)

1. 線程與進(jìn)程的區(qū)別:進(jìn)程由CPU調(diào)度,執(zhí)行計(jì)算機(jī)程序。線程由進(jìn)程調(diào)度,可獨(dú)立運(yùn)行。

2. 創(chuàng)建線程的方式,使用接口還是類:runnable接口、callable接口、thread類、線程池。使用接口好,接口開銷小且可以實(shí)現(xiàn)多繼承

  1. 實(shí)現(xiàn)runnable接口
    1. 重寫run方法
    2. 創(chuàng)建實(shí)現(xiàn)類實(shí)例,通過實(shí)現(xiàn)類實(shí)例創(chuàng)建Thread對象,Thread對象調(diào)用start使線程就緒
  2. 實(shí)現(xiàn)callable接口,獲取返回值
    1. 重寫call方法,設(shè)定返回值類型。
    2. 創(chuàng)建實(shí)現(xiàn)類實(shí)例,通過實(shí)現(xiàn)類實(shí)例創(chuàng)建FutureTask實(shí)例,使用FutureTask實(shí)例創(chuàng)建Thread對象,Thread對象調(diào)用start使線程就緒,F(xiàn)utureTask實(shí)例可以調(diào)用get方法獲取返回值
  3. 繼承Thread類
    1. 重寫run方法
    2. 創(chuàng)建子類實(shí)例,調(diào)用start使線程就緒
  4. 使用接口好,繼承Thread類開銷大,使用接口可以實(shí)現(xiàn)多繼承

3. 線程生命周期、守護(hù)線程:new、runnable、running、blocking、dead

  1. new新建:new Thread
  2. runnable可運(yùn)行:調(diào)用start方法后,等待cpu調(diào)度
  3. running運(yùn)行:執(zhí)行run方法??梢匀サ絙lock、runnable、terminated
    1. 調(diào)用wait、sleep、join會(huì)進(jìn)入block
    2. 調(diào)用stop會(huì)進(jìn)入terminated
  4. block阻塞:可以去到runnable、terminated
    1. 同步阻塞:synchronized,等待獲取鎖,回到runnable
    2. 等待阻塞:wait、sleep,回到runnable
    3. 其他阻塞:interrupted,回到runnable
    4. 調(diào)用stop,進(jìn)入terminated
  5. terminated終止
  6. 只要還有一個(gè)非守護(hù)線程未結(jié)束,守護(hù)線程就要全部工作。當(dāng)非守護(hù)線程全部結(jié)束,守護(hù)線程隨著JVM而停止。如GC線程就是守護(hù)線程。

4. 如何中斷一個(gè)線程:interrupt和isInterrupted。如果已經(jīng)調(diào)用了wait或sleep,會(huì)拋出異常

  1. 調(diào)用interrupt方法,可以通過isInterrupted判斷是否進(jìn)入中斷狀態(tài)
  2. 如果已經(jīng)調(diào)用了wait或sleep再調(diào)用interrupt,會(huì)拋出異常

5. wait、notify、notifyAll,wait和sleep的區(qū)別:是Object方法,wait會(huì)釋放鎖,notify拿到鎖執(zhí)行喚醒。sleep是Thread方法,不會(huì)釋放鎖

  1. 都是Object方法,wait必須在synchronized下使用,wait會(huì)讓線程進(jìn)入等待狀態(tài),會(huì)釋放鎖。當(dāng)其他線程執(zhí)行到notify時(shí),會(huì)喚醒等待的線程,使其重新進(jìn)入runnable狀態(tài)。
  2. 因?yàn)閣ait線程需要被notify喚醒,只有同一把鎖上等待的線程才能實(shí)現(xiàn)喚醒,因此wait和notify都屬于Object這個(gè)類鎖就可以保證wait線程一定能夠被notify喚醒。
  3. sleep是Thread方法,不會(huì)釋放鎖。wait是Object類方法,會(huì)釋放鎖

6. await、signal、signalAll:是JUC包下的Condition的方法,一個(gè)Lock可以綁定多個(gè)condition,實(shí)現(xiàn)精確喚醒

7. 線程間通信方式:synchronized、volatile、wait和notify、await和signal、join

  1. volatile:
    1. 讀:將工作內(nèi)存的變量副本設(shè)置為無效,要求線程去往主內(nèi)存中讀取最新的變量
    2. 寫:將工作內(nèi)存中修改后的變量刷新到主內(nèi)存
  2. synchronized:
    1. 讀:通過加鎖來,保證只有一個(gè)線程的工作內(nèi)存去往主內(nèi)存中讀取變量
    2. 寫:釋放鎖時(shí)將修改后的變量刷新到主內(nèi)存
  3. wait和notify
    1. 線程A調(diào)用wait進(jìn)入等待,線程B執(zhí)行notify喚醒線程A
  4. join
    1. 線程A調(diào)用join阻塞線程B,線程B等待線程A執(zhí)行完畢才能繼續(xù)執(zhí)行

8. 死鎖發(fā)生的條件、死鎖預(yù)防機(jī)制、排除死鎖:互斥、不剝奪、請求和保持、循環(huán)等待。一次性分配、有一個(gè)就不分配、可剝奪、資源有序分配。jps -l、jstack -l [deadlockId]

  1. 死鎖發(fā)生條件
    1. 互斥條件:互相鎖住對方需要的資源不釋放
    2. 不可剝奪條件:資源未使用完不會(huì)釋放
    3. 請求和保持條件:請求資源時(shí),不釋放持有的資源
    4. 循環(huán)等待條件:發(fā)生死鎖時(shí),進(jìn)入循環(huán)等待
  2. 死鎖預(yù)防
    1. 資源一次性分配:一次分配所有資源,不再請求。破壞請求條件
    2. 有一個(gè)資源就不分配:破壞了請求條件
    3. 可剝奪資源:先釋放資源才能請求資源,破壞不可剝奪條件
    4. 資源有序分配:將資源標(biāo)號,按需獲取,破壞循環(huán)等待條件
  3. 死鎖查找
    1. jps -l查看進(jìn)程
    2. jstack -l [id],查看進(jìn)程的堆棧情況,找到deadlock相關(guān)信息

9. 手寫死鎖

public class Main{

    public static Object a = new Object();
    public static Object b = new Object();

    public static void main(String[] args) {
        new Thread(new Lock1()).start();;
        new Thread(new Lock2()).start();
    }
}

class Lock1 implements Runnable{

    @Override
    public void run() {
        try{
            System.out.println("Lock1");
            while (true){
                synchronized (Main.a){
                    System.out.println("鎖住a");

                    Thread.sleep(2000);

                    synchronized (Main.b){
                        System.out.println("鎖住b");
                    }
                }
            }
        }catch (Exception e){
        }
    }
}

class Lock2 implements Runnable{

    @Override
    public void run() {
        try{
            System.out.println("Lock2");
            while (true){
                synchronized (Main.b){
                    System.out.println("鎖住b");

                    Thread.sleep(2000);

                    synchronized (Main.a){
                        System.out.println("鎖住a");
                    }
                }
            }
        }catch (Exception e){
        }
    }
}

10. 并發(fā)三大特性:原子性(synchronized)、內(nèi)存可見性(volatile和synchronized)、指令有序性(volatile和synchronized)。因?yàn)関olatile不能保證原子性,索引volatile并不是線程安全的

  1. 原子性:對于原子操作要能確保其結(jié)果正確,如i++就不是原子性的操作。讀取i的值,i+1,i=i+1三步。synchronized可以實(shí)現(xiàn)原子性,volatile不能保證,因此volatile不是線程安全
  2. 可見性:一般指的是變量可見性,即一個(gè)線程對變量的修改其他線程可見。synchronized和volatile
  3. 有序性:一般指指令有序性,synchronized通過加鎖,volatile通過內(nèi)存屏障

11. synchronized鎖作用域:

  1. 非靜態(tài)方法對象鎖,不同對象調(diào)用不同對象鎖可以交替執(zhí)行。
  2. 靜態(tài)方法類鎖,不同類對象調(diào)用不同類鎖無法交替執(zhí)行。
  3. 類鎖和對象鎖互不干擾

12. synchronized鎖升級:無鎖、偏向鎖、輕量級鎖、重量級鎖

  1. 無鎖
  2. 偏向鎖:當(dāng)一個(gè)線程多次獲得同一把鎖,那么它不需要參與爭搶,可以通過重入鎖機(jī)制實(shí)現(xiàn)多次持有(RentrantLock和synchronized都是可以重入鎖。ReentrantLock會(huì)記錄當(dāng)前線程,以便重入)
  3. 輕量級鎖:當(dāng)線程A持有偏向鎖,線程B也要參與爭搶時(shí),偏向鎖升級為輕量級鎖
  4. 重量級鎖:當(dāng)線程A持有偏向鎖,線程B也要爭搶這個(gè)鎖時(shí),線程B會(huì)通過CAS方式爭搶,如果失敗,進(jìn)入自旋。當(dāng)線程A處在偏向鎖,線程B在進(jìn)行自旋時(shí),線程C也要爭搶這個(gè)鎖,那么輕量級鎖會(huì)升級為重量級鎖

13. 樂觀鎖和悲觀鎖:CAS樂觀鎖,不加鎖執(zhí)行。synchronized和ReentrantLock悲觀鎖,加鎖執(zhí)行。

  1. 樂觀鎖:認(rèn)為不會(huì)發(fā)生沖突,不加鎖,發(fā)生沖突,進(jìn)行重試,直到成功
  2. 悲觀鎖:先加鎖再操作。synchronized和ReentrantLock

14. CAS自旋鎖,ABA問題,自旋消耗資源問題,哪些地方用到了CAS

  1. CAS是CompareAndSwap,比較再交換。CAS是一種無鎖機(jī)制,有三個(gè)操作數(shù),內(nèi)存值、預(yù)期值、新值。當(dāng)且僅當(dāng)內(nèi)存值等于預(yù)期值時(shí),才會(huì)將內(nèi)存值設(shè)置為新值。否則進(jìn)行自旋
  2. CAS自旋消耗:CAS是CPU級別的操作,原子性操作,速度快,但是如果一直自旋,CPU占用高??梢栽O(shè)定一個(gè)自旋上限。
  3. ABA問題:如果一個(gè)值原本是A,后來被改為B,最后又被改回A。比較的時(shí)候會(huì)認(rèn)為A沒有變,對A進(jìn)行交換。但實(shí)際上A已經(jīng)被修改??梢酝ㄟ^原子類的版本號AtomicStampedReference在變量前加一個(gè)版本號,ABA變成1A2B3A
  4. synchronized的鎖升級過程用到了CAS。hashMap1.8之后的put用到CAS。原子類的自增方法用到CAS。
  5. 可以通過Unsafe類調(diào)用CAS

15. ReentrantLock和synchronized的區(qū)別:可重入、實(shí)現(xiàn)方式、性能、等待可中斷、公平鎖和非公平鎖、精確喚醒、使用優(yōu)先級

  1. 可重入:synchronized和ReentrantLock都是可重入
    1. synchronized通過aqs維護(hù)的state鎖狀態(tài)實(shí)現(xiàn),0代表可以重入,1代表不能重入
    2. ReentrantLock通過tryLock來實(shí)現(xiàn)
  2. 實(shí)現(xiàn)方式
    1. synchronized是通過JVM
    2. ReentrantLock是通過jdk
  3. 性能:synchronized在1.6版本經(jīng)過一次鎖升級優(yōu)化,性能和ReentrantLock相同
  4. 等待可中斷
    1. synchronized不行
    2. ReentrantLock可以,通過lockInterruptibly方法
  5. 公平鎖和非公平鎖
    1. ReentrantLock可以實(shí)現(xiàn)公平鎖和非公平鎖,默認(rèn)實(shí)現(xiàn)非公平鎖,避免線程切換,效率更高
    2. synchronized只能實(shí)現(xiàn)非公平鎖
  6. 精確喚醒
    1. ReentrantLock可以綁定多個(gè)condition實(shí)現(xiàn)精確喚醒
    2. synchronized只能隨機(jī)喚醒wait線程
  7. 優(yōu)先級
    1. 如果不是需要使用ReentrantLock的高級功能,優(yōu)先使用synchronized,因?yàn)閟ynchronized是基于JVM實(shí)現(xiàn)的,不需要手動(dòng)釋放鎖,而ReentrantLock要手動(dòng)釋放鎖,否則會(huì)造成死鎖

16. 手撕:ReentrantLock精確喚醒輪番打印10次ABC

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

public class Main{

    //CountDownLatch
    private static int num = 1;
    private static final CountDownLatch latch = new CountDownLatch(10);

    //創(chuàng)建ReentrantLock,綁定3個(gè)condition
    private static final ReentrantLock lock = new ReentrantLock();
    private static final Condition A = lock.newCondition();
    private static final Condition B = lock.newCondition();
    private static final Condition C = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        //初始化countdownlatch數(shù)量
        long loop = latch.getCount();

        //啟動(dòng)線程
        new Thread(() ->{
            for(int i=1;i<=loop;i++){
                try {
                    printA();
                }catch (Exception e){
                }
            }
        },"A").start();

        new Thread(() ->{
            for(int i=1;i<=loop;i++){
                try {
                    printB();
                }catch (Exception e){
                }
            }
        },"B").start();

        new Thread(() ->{
            for(int i=1;i<=loop;i++){
                try {
                    printC(i);
                }catch (Exception e){
                }
            }
        },"C").start();

        //當(dāng)count計(jì)數(shù)為0時(shí),終止
        latch.await();
    }

    public static void printA(){
        try{
            lock.lock();
            //當(dāng)num為1時(shí),打印A
            if(num != 1){
                A.await();
            }

            System.out.print(Thread.currentThread().getName());
            //num改為2
            num = 2;
            //通知B打印
            B.signal();
        }catch (Exception e){
        }finally {
            lock.unlock();
        }
    }

    public static void printB(){
        try{
            lock.lock();
            //當(dāng)num為2時(shí),打印B
            if(num != 2){
                B.await();
            }

            System.out.print(Thread.currentThread().getName());
            //num改為3
            num = 3;
            //通知C打印
            C.signal();
        }catch (Exception e){
        }finally {
            lock.unlock();
        }
    }

    public static void printC(long loop){
        try{
            lock.lock();
            //當(dāng)num為3時(shí),打印C
            if(num != 3){
                C.await();
            }

            System.out.print(Thread.currentThread().getName());
            //第幾輪
            System.out.println("["+ loop+"]");

            //num改為1
            num = 1;
            //通知A打印
            A.signal();
            //計(jì)數(shù)-1
            latch.countDown();
        }catch (Exception e){
        }finally {
            lock.unlock();
        }
    }
}

17. ReentrantLock實(shí)現(xiàn)公平鎖和非公平鎖:通過AQS維護(hù)的volatile鎖狀態(tài)和阻塞隊(duì)列

  1. 公平鎖:只有阻塞隊(duì)列頭部的線程才能獲取鎖。
    1. 創(chuàng)建公平鎖,ReentrantLock(true)
    2. 判斷是公平鎖還是非公平鎖
    3. fairSyn:先獲取當(dāng)前線程和AQS中維護(hù)的volatile鎖狀態(tài)和同步隊(duì)列。如果鎖狀態(tài)為0,表明鎖可以獲取??梢酝ㄟ^CAS方式嘗試將鎖狀態(tài)設(shè)置為1,同時(shí)也會(huì)判斷當(dāng)前線程是否是同步隊(duì)列的頭線程,如果不是,那么就算CAS成功也不能獲取鎖,返回false。如果線程是頭線程,會(huì)記錄當(dāng)前線程,以便重入。
  2. 非公平鎖:當(dāng)前線程只需要完成CAS就可以獲取鎖
    1. 創(chuàng)建非公平鎖:ReentrantLock()
    2. nofairSync:先獲取當(dāng)前線程和AQS中維護(hù)的volatile鎖狀態(tài)。如果鎖狀態(tài)為0,表明鎖可以被獲取,通過CAS方式嘗試將狀態(tài)設(shè)置為1,如果成功,記錄當(dāng)前線程,以便后續(xù)重入。

18. AQS是什么,哪些地方用到了AQS

  1. AQS是隊(duì)列同步器,維護(hù)了一個(gè)volatile int鎖狀態(tài)和線程同步隊(duì)列來管理線程。有三個(gè)主要方法,getState、setState、compareAndSetState
  2. 兩種模式:獨(dú)占和共享
  3. ReentrantLock的公平鎖和非公平鎖

19. volatile特性:不能保證原子性、內(nèi)存可見性、指令有序性

20. volatile是線程安全的嗎,volatile為什么不能保證原子性

  1. 因?yàn)関olatile不能保證原子性,所以不是線程安全的
  2. 因?yàn)閷τ诜窃有圆僮?,如果i++,volatile無法保證其在多線程下的執(zhí)行結(jié)果是準(zhǔn)確的。如果有兩個(gè)線程都讀取了i++這個(gè)操作,本來i=5,進(jìn)行兩次i++應(yīng)該返回i=7,但是兩個(gè)線程分布執(zhí)行一次i++,返回結(jié)果為i=6。因此無法保證原子性。而synchronized通過加鎖確保線程只能有序執(zhí)行操作,釋放鎖會(huì)將執(zhí)行結(jié)果刷新到主內(nèi)存

21. volatile內(nèi)存可見性如何實(shí)現(xiàn)

  1. volatile讀:JMM會(huì)將工作內(nèi)存中的變量副本設(shè)置為無效,要求線程去往主內(nèi)存中讀取最新的變量
  2. volatile寫:JMMV會(huì)將線程在工作內(nèi)存中修改后的值刷新到主內(nèi)存中

22. volatile指令有序性如何實(shí)現(xiàn):內(nèi)存屏障

  1. volatile寫前:確保之前的寫都已經(jīng)刷新到主內(nèi)存中
  2. volatile寫后:禁止與后面的volatile操作重排
  3. volatile讀前:禁止與后面的讀重排
  4. volatile讀后:禁止與后面的寫重排

23. synchronized和volatile比較:線程安全、作用域、不能保證原子性、線程阻塞

  1. 作用域:volatile僅能作用在變量,synchronized可以作用在變量、方法、類
  2. 線程阻塞:synchronized會(huì)阻塞,volatile不會(huì)阻塞
  3. 線程安全:volatile不能保證原子性,不是線程安全。synchronized是線程安全

24. ThreadLocal底層實(shí)現(xiàn):每個(gè)Thread都有一個(gè)ThreadLocalMap

  1. 底層實(shí)現(xiàn):每個(gè)線程都有一個(gè)ThreadLocalMap,key為ThreadLocal對象,value為傳入的對象
  2. 實(shí)現(xiàn)線程間數(shù)據(jù)隔離,父子線程間也無法通信
  3. set方法
    1. 先獲取當(dāng)前線程,判斷ThreadLocalMap中是否存在當(dāng)前線程,如果存在,更新。否則新建
  4. get方法
    1. 先獲取當(dāng)前線程,判斷ThreadLocalMap中是否存在當(dāng)前線程,如果存在,返回value。否則返回預(yù)設(shè)值
  5. remove方法
    1. 將當(dāng)前線程從ThreadLocalMap中移除

25. ThreadLocal和synchronized的區(qū)別:都是解決高并發(fā)下訪問變量問題

  1. ThreadLocal通過將線程和變量進(jìn)行綁定來解決,采用空間換時(shí)間策略
  2. synchronized通過加鎖來使線程有序訪問變量,采用時(shí)間換空間策略
  3. ThreadLocal不是線程安全的,如果threadlocal.get()獲取當(dāng)前線程后使用到了其他線程,可能會(huì)出現(xiàn)多線程修改同一個(gè)變量問題。

26. ThreadLocal的內(nèi)存泄漏問題,如何解決:

  1. 因?yàn)門hrealLocalMap的key是this指代的ThrealLocal實(shí)例,是弱引用,會(huì)被gc回收。而value是new創(chuàng)建的強(qiáng)引用,不會(huì)被gc回收。key被回收之后變?yōu)閚ull,無法獲取到value,造成浪費(fèi)。
  2. 可以手動(dòng)調(diào)用remove方法將當(dāng)前線程從Map中移除

27. 為什么要用線程池:線程可復(fù)用,減少創(chuàng)建和銷毀。大量創(chuàng)建線程可能導(dǎo)致OOM

28. 線程池創(chuàng)建方式、線程池三大類型、七大參數(shù)、四種拒絕策略

  1. Executors創(chuàng)建

  2. ThreadPoolExecutor自定義線程池參數(shù)

  3. SingleThreadExecutor單例線程池

  4. FixedThreadPool固定數(shù)量線程池

  5. CachedThreadPool可伸縮數(shù)量線程池

  6. ScheduledThreadPool固定數(shù)量的定時(shí)線程池

  7. corePoolSize:核心線程池大小,空閑時(shí),隊(duì)列滿了,會(huì)超出

  8. maximumPoolSize:可同時(shí)活動(dòng)的線程數(shù)量。超過該值,線程停止

  9. keepalivetime:最大存活時(shí)間

  10. uint:時(shí)間單位

  11. workqueue:同步隊(duì)列,數(shù)組、鏈表、優(yōu)先隊(duì)列

  12. threadfactory:線程工廠

  13. handler:拒絕策略

  14. abortPolicy:超出承載,不接受后續(xù),拋出異常

  15. callerRunPolicy:超出承載,使用main線程

  16. discardPolicy:超出承載,不接受后續(xù),不拋異常

  17. discardOldest:超出承載,拋棄最老的線程

29. maximumPoolSize如何設(shè)置

  1. 使用CPU密集型:Runtime.getRuntime().avaliableProcessors

30. 為什么不能用Executors創(chuàng)建線程池:

  1. SingleThreadExecutors和FixedThreadPool的LinkedBlockingQueue的容量為Integer.MAX_VALUE,可能堆積大量線程,造成OOM。
  2. CachedThreadPool的maximumPoolSize為Integer.MAX_VALUE,可能創(chuàng)建大量線程,造成OOM

31. 手撕:消費(fèi)者生產(chǎn)者模型:Lock和Condition實(shí)現(xiàn)、阻塞隊(duì)列實(shí)現(xiàn)、兩個(gè)線程打印1A2B3C4D5E6F

  1. 生產(chǎn)者消費(fèi)者模型
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Main{
    public static void main(String[] args) {

        Test test = new Test();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    test.producer();
                }
            }
        }).start();
        
        new Thread(new Runnable() {
            @Override
            public void run() {
                while (true){
                    test.consumer();
                }
            }
        }).start();
    }
}

class Test{
    //使用Lock和Conditoon
    private Lock lock = new ReentrantLock();
    private Condition consumer = lock.newCondition();
    private Condition producer = lock.newCondition();

    //控制標(biāo)志flag。true消費(fèi),停止生產(chǎn)。false生產(chǎn),停止消費(fèi)
    private boolean flag = false;

    public void producer(){
        try{
            lock.lock();
            //判斷flag是否為true,如果為true,說明正在消費(fèi),停止生產(chǎn)
            if(flag == true){
                producer.await();
            }
            System.out.println("生產(chǎn)");
            Thread.sleep(1000);
            //flag改為true,通知消費(fèi)
            flag = true;
            consumer.signal();
        }catch (Exception e){
        }finally {
            lock.unlock();
        }
    }

    public void consumer(){
        try{
            lock.lock();
            //如果flag為false,說明正在生產(chǎn),停止消費(fèi)
            if(flag == false){
                consumer.await();
            }
            System.out.println("消費(fèi)");
            Thread.sleep(1000);
            //flag改為false,通知生產(chǎn)
            flag = false;
            producer.signal();
        }catch (Exception e){
        }finally {
            lock.unlock();
        }
    }
}
  1. 阻塞隊(duì)列實(shí)現(xiàn)生產(chǎn)者消費(fèi)者
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.BlockingQueue;

public class Main{
    public static void main(String[] args) {

        //初始化一個(gè)阻塞隊(duì)列,使用數(shù)組類型
        BlockingQueue<String> queue = new ArrayBlockingQueue<>(10);

        new Thread(new producer(queue),"生產(chǎn)者").start();
        new Thread(new consumer(queue),"消費(fèi)者").start();
    }
}

class producer implements Runnable{

    //創(chuàng)建一個(gè)阻塞隊(duì)列,初始化為傳入的queue
    BlockingQueue<String> queue;
    producer(BlockingQueue queue){
        this.queue = queue;
    }

    @Override
    public void run() {
        int index = 0;
        while (true){
            //初始化資源
            String product = String.valueOf(index);

            //put放入隊(duì)列
            try {
                queue.put(product);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //打印
            System.out.println(Thread.currentThread().getName()+"生產(chǎn)了"+product+",剩余商品數(shù)量:"+queue.size());

            //等待1秒,等消費(fèi)者消費(fèi)
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            index++;
        }
    }
}

class consumer implements Runnable{

    //創(chuàng)建一個(gè)阻塞隊(duì)列,初始化為傳入的queue
    BlockingQueue<String> queue;
    consumer(BlockingQueue queue){
        this.queue = queue;
    }

    @Override
    public void run() {
        while (true){
            //直接從隊(duì)列中take
            try {
                System.out.println(Thread.currentThread().getName()+"消費(fèi)"+queue.take()+",剩余商品數(shù)量:"+queue.size());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            //等待1秒,等生產(chǎn)者生產(chǎn)
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
  1. 雙線程打印1A2B3C

public class Main{
    public static void main(String[] args) {
        //數(shù)字字符數(shù)組
        char[] c1 = "123456".toCharArray();
        //字母字符數(shù)組
        char[] c2 = "ABCDEF".toCharArray();

        //創(chuàng)建一個(gè)Object對象
        Object object = new Object();

        //打印數(shù)字
        new Thread(() ->{
            synchronized (object){
                //遍歷數(shù)字?jǐn)?shù)字
                for(char c : c1){
                    System.out.print(c);

                    //喚醒另一個(gè)線程
                    object.notify();

                    //當(dāng)前線程wait等待
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //打印完之后終止該線程
                object.notify();
            }
        }).start();

        //打印字母
        new Thread(() ->{
            synchronized (object){
                for(char c : c2){
                    System.out.print(c);

                    //喚醒另一個(gè)線程
                    object.notify();

                    //當(dāng)前線程等待
                    try {
                        object.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }

                //遍歷完之后終止當(dāng)前線程
                object.notify();
            }
        }).start();
    }
}
?著作權(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ù)。

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