欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java多線程連續(xù)打印abc實(shí)現(xiàn)方法詳解

 更新時間:2020年03月24日 11:06:19   作者:平凡希  
這篇文章主要介紹了Java多線程連續(xù)打印abc實(shí)現(xiàn)方法詳解,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

一道編程題如下:

實(shí)例化三個線程,一個線程打印a,一個線程打印b,一個線程打印c,三個線程同時執(zhí)行,要求打印出10個連著的abc。

題目分析:

通過題意我們可以得出,本題需要我們使用三個線程,三個線程分別會打印6次字符,關(guān)鍵是如何保證順序一定是abc...呢。所以此題需要同步機(jī)制來解決問題!

令打印字符A的線程為ThreadA,打印B的ThreadB,打印C的為ThreadC。問題為三線程間的同步喚醒操作,主要的目的就是使程序按ThreadA->ThreadB->ThreadC->ThreadA循環(huán)執(zhí)行三個線程,因此本人整理出了三種方式來解決此問題。

一、通過兩個鎖(不推薦,可讀性和安全性比較差)

package com.demo.test;

/**
 * 基于兩個lock實(shí)現(xiàn)連續(xù)打印abcabc....
 * @author lixiaoxi
 *
 */
public class TwoLockPrinter implements Runnable {

  // 打印次數(shù)
  private static final int PRINT_COUNT = 10;
  // 前一個線程的打印鎖
  private final Object fontLock;
  // 本線程的打印鎖
  private final Object thisLock;
  // 打印字符
  private final char printChar;

  public TwoLockPrinter(Object fontLock, Object thisLock, char printChar) {
    this.fontLock = fontLock;
    this.thisLock = thisLock;
    this.printChar = printChar;
  }

  @Override
  public void run() {
    // 連續(xù)打印PRINT_COUNT次
    for (int i = 0; i < PRINT_COUNT; i++) {
      // 獲取前一個線程的打印鎖
      synchronized (fontLock) {
        // 獲取本線程的打印鎖
        synchronized (thisLock) {
          //打印字符
          System.out.print(printChar);
          // 通過本線程的打印鎖喚醒后面的線程 
          // notify和notifyall均可,因?yàn)橥粫r刻只有一個線程在等待
          thisLock.notify();
        }
        // 不是最后一次則通過fontLock等待被喚醒
        // 必須要加判斷,不然雖然能夠打印10次,但10次后就會直接死鎖
        if(i < PRINT_COUNT - 1){
          try {
            // 通過fontLock等待被喚醒
            fontLock.wait();
            
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }
        
      }  
    }  
  }

  public static void main(String[] args) throws InterruptedException {
    // 打印A線程的鎖
    Object lockA = new Object();
    // 打印B線程的鎖
    Object lockB = new Object();
    // 打印C線程的鎖
    Object lockC = new Object();
    
    // 打印a的線程
    Thread threadA = new Thread(new TwoLockPrinter(lockC, lockA, 'A'));
    // 打印b的線程
    Thread threadB = new Thread(new TwoLockPrinter(lockA, lockB, 'B'));
    // 打印c的線程
    Thread threadC = new Thread(new TwoLockPrinter(lockB, lockC, 'C'));

    // 依次開啟a b c線程
    threadA.start();
    Thread.sleep(100); // 確保按順序A、B、C執(zhí)行
    threadB.start();
    Thread.sleep(100);
    threadC.start();
    Thread.sleep(100);
  }

}

打印結(jié)果:

ABCABCABCABCABCABCABCABCABCABC

分析:

此解法為了為了確定喚醒、等待的順序,每一個線程必須同時持有兩個對象鎖,才能繼續(xù)執(zhí)行。一個對象鎖是fontLock,就是前一個線程所持有的對象鎖,還有一個就是自身對象鎖thisLock。主要的思想就是,為了控制執(zhí)行的順序,必須要先持有fontLock鎖,也就是前一個線程要釋放掉前一個線程自身的對象鎖,當(dāng)前線程再去申請自身對象鎖,兩者兼?zhèn)鋾r打印,之后首先調(diào)用thisLock.notify()釋放自身對象鎖,喚醒下一個等待線程,再調(diào)用fontLock.wait()釋放prev對象鎖,暫停當(dāng)前線程,等待再次被喚醒后進(jìn)入循環(huán)。運(yùn)行上述代碼,可以發(fā)現(xiàn)三個線程循環(huán)打印ABC,共10次。程序運(yùn)行的主要過程就是A線程最先運(yùn)行,持有C,A對象鎖,后釋放A鎖,喚醒B。線程B等待A鎖,再申請B鎖,后打印B,再釋放B鎖,喚醒C,線程C等待B鎖,再申請C鎖,后打印C,再釋放C鎖,喚醒A??雌饋硭坪鯖]什么問題,但如果你仔細(xì)想一下,就會發(fā)現(xiàn)有問題,就是初始條件,三個線程按照A,B,C的順序來啟動,按照前面的思考,A喚醒B,B喚醒C,C再喚醒A。但是這種假設(shè)依賴于JVM中線程調(diào)度、執(zhí)行的順序,所以需要手動控制他們?nèi)齻€的啟動順序,即Thread.Sleep(100)。

二、通過一個ReentrantLock和三個conditon實(shí)現(xiàn)(推薦,安全性,性能和可讀性較高)

package com.demo.test;

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

/**
 * 基于一個ReentrantLock和三個conditon實(shí)現(xiàn)連續(xù)打印abcabc...
 * @author lixiaoxi
 *
 */
public class RcSyncPrinter implements Runnable{

  // 打印次數(shù)
  private static final int PRINT_COUNT = 10;
  // 打印鎖
  private final ReentrantLock reentrantLock;
  // 本線程打印所需的condition
  private final Condition thisCondtion;
  // 下一個線程打印所需要的condition
  private final Condition nextCondtion;
  // 打印字符
  private final char printChar;

  public RcSyncPrinter(ReentrantLock reentrantLock, Condition thisCondtion, Condition nextCondition, 
      char printChar) {
    this.reentrantLock = reentrantLock;
    this.nextCondtion = nextCondition;
    this.thisCondtion = thisCondtion;
    this.printChar = printChar;
  }

  @Override
  public void run() {
    // 獲取打印鎖 進(jìn)入臨界區(qū)
    reentrantLock.lock();
    try {
      // 連續(xù)打印PRINT_COUNT次
      for (int i = 0; i < PRINT_COUNT; i++) {
        //打印字符
        System.out.print(printChar);
        // 使用nextCondition喚醒下一個線程
        // 因?yàn)橹挥幸粋€線程在等待,所以signal或者signalAll都可以
        nextCondtion.signal();
        // 不是最后一次則通過thisCondtion等待被喚醒
        // 必須要加判斷,不然雖然能夠打印10次,但10次后就會直接死鎖
        if (i < PRINT_COUNT - 1) {
          try {
            // 本線程讓出鎖并等待喚醒
            thisCondtion.await();
          } catch (InterruptedException e) {
            e.printStackTrace();
          }
        }

      }
    } finally {
      // 釋放打印鎖
      reentrantLock.unlock();
    }
  }
  
  public static void main(String[] args) throws InterruptedException {
    // 寫鎖
    ReentrantLock lock = new ReentrantLock();
    // 打印a線程的condition
    Condition conditionA = lock.newCondition();
    // 打印b線程的condition
    Condition conditionB = lock.newCondition();
    // 打印c線程的condition
    Condition conditionC = lock.newCondition();
    // 實(shí)例化A線程
    Thread printerA = new Thread(new RcSyncPrinter(lock, conditionA, conditionB, 'A'));
    // 實(shí)例化B線程
    Thread printerB = new Thread(new RcSyncPrinter(lock, conditionB, conditionC, 'B'));
    // 實(shí)例化C線程
    Thread printerC = new Thread(new RcSyncPrinter(lock, conditionC, conditionA, 'C'));
    // 依次開始A B C線程
    printerA.start();
    Thread.sleep(100);
    printerB.start();
    Thread.sleep(100);
    printerC.start();
  }
}

打印結(jié)果:

ABCABCABCABCABCABCABCABCABCABC

分析:

仔細(xì)想想本問題,既然同一時刻只能有一個線程打印字符,那我們?yōu)槭裁床皇褂靡粋€同步鎖ReentrantLock?線程之間的喚醒操作可以通過Condition實(shí)現(xiàn),且Condition可以有多個,每個condition.await阻塞只能通過該condition的signal/signalall來喚醒!這是synchronized關(guān)鍵字所達(dá)不到的,那我們就可以給每個打印線程一個自身的condition和下一個線程的condition,每次打印字符后,調(diào)用下一個線程的condition.signal來喚醒下一個線程,然后自身再通過自己的condition.await來釋放鎖并等待喚醒。

三、通過一個鎖和一個狀態(tài)變量來實(shí)現(xiàn)(推薦)

package com.demo.test;

/**
 * 基于一個鎖和一個狀態(tài)變量實(shí)現(xiàn)連續(xù)打印abcabc...
 * @author lixiaoxi
 *
 */
public class StateLockPrinter {
  //狀態(tài)變量
  private volatile int state=0;
  
  // 打印線程
  private class Printer implements Runnable {
    //打印次數(shù)
    private static final int PRINT_COUNT=10;
    //打印鎖
    private final Object printLock;
    //打印標(biāo)志位 和state變量相關(guān)
    private final int printFlag;
    //后繼線程的線程的打印標(biāo)志位,state變量相關(guān)
    private final int nextPrintFlag;
    //該線程的打印字符
    private final char printChar;
    public Printer(Object printLock, int printFlag,int nextPrintFlag, char printChar) {
      super();
      this.printLock = printLock;
      this.printFlag=printFlag;
      this.nextPrintFlag=nextPrintFlag;
      this.printChar = printChar;
    }

    @Override
    public void run() {
      //獲取打印鎖 進(jìn)入臨界區(qū)
      synchronized (printLock) {
        //連續(xù)打印PRINT_COUNT次
        for(int i=0;i<PRINT_COUNT;i++){
          //循環(huán)檢驗(yàn)標(biāo)志位 每次都阻塞然后等待喚醒
          while (state!=printFlag) {
            try {
              printLock.wait();
            } catch (InterruptedException e) {
              return;
            }
          }
          //打印字符
          System.out.print(printChar);
          //設(shè)置狀態(tài)變量為下一個線程的標(biāo)志位
          state=nextPrintFlag;
          //注意要notifyall,不然會死鎖,因?yàn)閚otify只通知一個,
          //但是同時等待的是兩個,如果喚醒的不是正確那個就會沒人喚醒,死鎖了
          printLock.notifyAll();
        }
      }
    }
    
  }

  public void test() throws InterruptedException{
    //鎖
    Object lock=new Object();
    //打印A的線程
    Thread threadA=new Thread(new Printer(lock, 0,1, 'A'));
    //打印B的線程
    Thread threadB=new Thread(new Printer(lock, 1,2, 'B'));
    //打印C的線程
    Thread threadC=new Thread(new Printer(lock, 2,0, 'C'));
    //一次啟動A B C線程
    threadA.start();
    Thread.sleep(1000);
    threadB.start();
    Thread.sleep(1000);
    threadC.start();
  }
  
  public static void main(String[] args) throws InterruptedException {
    
    StateLockPrinter print = new StateLockPrinter();
    print.test();
  }
  
}

打印結(jié)果:

ABCABCABCABCABCABCABCABCABCABC

分析:

狀態(tài)變量是一個volatile的整型變量,0代表打印a,1代表打印b,2代表打印c,三個線程都循環(huán)檢驗(yàn)標(biāo)志位,通過阻塞前和阻塞后兩次判斷可以確保當(dāng)前打印的正確順序,隨后線程打印字符,然后設(shè)置下一個狀態(tài)字符,喚醒其它線程,然后重新進(jìn)入循環(huán)。

補(bǔ)充題

三個Java多線程循環(huán)打印遞增的數(shù)字,每個線程打印5個數(shù)值,打印周期1-75,同樣的解法:

package com.demo.test;

import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 數(shù)字打印,三個線程同時打印數(shù)字,第一個線程打印12345,第二個線程打印678910 .........
 * @author lixiaoxi
 *
 */
public class NumberPrinter {

  //打印計數(shù)器
  private final AtomicInteger counter=new AtomicInteger(0);
  
  private class Printer implements Runnable{
    //總共需要打印TOTAL_PRINT_COUNT次
    private static final int TOTAL_PRINT_COUNT = 5;
    //每次打印PER_PRINT_COUNT次
    private static final int PER_PRINT_COUNT = 5;
    //打印鎖
    private final ReentrantLock reentrantLock;
    //前一個線程的condition
    private final Condition afterCondition;
    //本線程的condition
    private final Condition thisCondtion;
    
    public Printer(ReentrantLock reentrantLock, Condition thisCondtion,Condition afterCondition) {
      super();
      this.reentrantLock = reentrantLock;
      this.afterCondition = afterCondition;
      this.thisCondtion = thisCondtion;
    }

    @Override
    public void run() {
      //進(jìn)入臨界區(qū)
      reentrantLock.lock();
      try {
        //循環(huán)打印TOTAL_PRINT_COUNT次
        for(int i=0;i<TOTAL_PRINT_COUNT;i++){
          //打印操作
          for(int j=0;j<PER_PRINT_COUNT;j++){
            //以原子方式將當(dāng)前值加 1。
            //incrementAndGet返回的是新值(即加1后的值)
            System.out.println(counter.incrementAndGet());
          }
          //通過afterCondition通知后面線程
          afterCondition.signalAll();
          if(i < TOTAL_PRINT_COUNT - 1){
            try {
              //本線程釋放鎖并等待喚醒
              thisCondtion.await();
            } catch (InterruptedException e) {
              e.printStackTrace();
            }
          }
        }
      } finally {
        reentrantLock.unlock();
      }
    }
  }
  
  public void test() throws InterruptedException {
    //打印鎖
    ReentrantLock reentrantLock=new ReentrantLock();
    //打印A線程的Condition
    Condition conditionA=reentrantLock.newCondition();
    //打印B線程的Condition
    Condition conditionB=reentrantLock.newCondition();
    //打印C線程的Condition
    Condition conditionC=reentrantLock.newCondition();

    //打印線程A
    Thread threadA=new Thread(new Printer(reentrantLock,conditionA, conditionB));
    //打印線程B
    Thread threadB=new Thread(new Printer(reentrantLock, conditionB, conditionC));
    //打印線程C
    Thread threadC=new Thread(new Printer(reentrantLock, conditionC, conditionA));
    // 依次開啟a b c線程
    threadA.start();
    Thread.sleep(100);
    threadB.start();
    Thread.sleep(100);
    threadC.start();
  }
  
  public static void main(String[] args) throws InterruptedException {
    NumberPrinter print = new NumberPrinter();
    print.test();
  }
}

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

2
4
6
8
10
12
14
16
18
20
22
24
26
28
30
32
34
36
38
40
42
44
46
48
50
52
54
56
58
60
62
64
66
68
70
72
74

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java EE中的編碼問題及解決方案(總結(jié))

    Java EE中的編碼問題及解決方案(總結(jié))

    在JavaEE開發(fā)中,處理字符編碼問題是確保數(shù)據(jù)準(zhǔn)確傳輸和顯示的關(guān)鍵,常見的編碼問題包括表單提交亂碼、JSP頁面編碼、請求參數(shù)編碼不一致等,本文給大家介紹Java EE中的編碼問題及解決方案,感興趣的朋友一起看看吧
    2024-10-10
  • Java/Android 實(shí)現(xiàn)簡單的HTTP服務(wù)器

    Java/Android 實(shí)現(xiàn)簡單的HTTP服務(wù)器

    這篇文章主要介紹了Java/Android 如何實(shí)現(xiàn)簡單的HTTP服務(wù)器,幫助大家更好的進(jìn)行功能測試,感興趣的朋友可以了解下
    2020-10-10
  • CMD運(yùn)行Intellij Idea編譯后的class文件操作

    CMD運(yùn)行Intellij Idea編譯后的class文件操作

    這篇文章主要介紹了CMD運(yùn)行Intellij Idea編譯后的class文件操作,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Netty分布式客戶端接入流程初始化源碼分析

    Netty分布式客戶端接入流程初始化源碼分析

    這篇文章主要介紹了Netty分布式客戶端接入流程初始化源碼分析,有關(guān)channelConfig有關(guān)的初始化過程剖析,有需要的朋友可以借鑒參考下,希望能夠有所幫助
    2022-03-03
  • Java的單例設(shè)計模式詳解

    Java的單例設(shè)計模式詳解

    今天小編就為大家分享一篇關(guān)于Java的單例設(shè)計模式詳解,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2018-12-12
  • SpringBoot整合mybatis常見問題(小結(jié))

    SpringBoot整合mybatis常見問題(小結(jié))

    這篇文章主要介紹了SpringBoot整合mybatis常見問題(小結(jié)),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-12-12
  • spring?@value無法取值多個properties文件的解決

    spring?@value無法取值多個properties文件的解決

    這篇文章主要介紹了spring?@value無法取值多個properties文件的解決,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • Log4j不同模塊輸出到不同的文件中

    Log4j不同模塊輸出到不同的文件中

    這篇文章主要介紹了Log4j不同模塊輸出到不同的文件中 的相關(guān)資料,需要的朋友可以參考下
    2016-08-08
  • Java流程控制之循環(huán)結(jié)構(gòu)for,增強(qiáng)for循環(huán)

    Java流程控制之循環(huán)結(jié)構(gòu)for,增強(qiáng)for循環(huán)

    這篇文章主要介紹了Java流程控制之循環(huán)結(jié)構(gòu)for,增強(qiáng)for循環(huán),for循環(huán)是編程語言中一種循環(huán)語句,而循環(huán)語句由循環(huán)體及循環(huán)的判定條件兩部分組成,其表達(dá)式為:for(單次表達(dá)式;條件表達(dá)式;末尾循環(huán)體){中間循環(huán)體;},下面我們倆看看文章內(nèi)容的詳細(xì)介紹
    2021-12-12
  • springboot如何使用assembly打包項(xiàng)目和啟動腳本

    springboot如何使用assembly打包項(xiàng)目和啟動腳本

    這篇文章主要介紹了springboot如何使用assembly打包項(xiàng)目和啟動腳本問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2023-06-06

最新評論