面試題-使用執行緒交替列印奇數偶數
阿新 • • 發佈:2019-05-21
這世上有三樣東西是別人搶不走的:一是吃進胃裡的食物,二是藏在心中的夢想,三是讀進大腦的書
- 分析題目。需要使用兩個執行緒交替列印奇偶數。
- 使用同步鎖解決這個問題
- 使用訊號量來實現交替列印
- 定義兩個訊號量,一個奇數訊號量,一個偶數訊號量,都初始化為1
- 先用掉偶數的訊號量,因為要讓奇數先啟動,等奇數列印完再釋放
訊號量實現
- 具體實現思路:
- 定義兩個訊號量,一個奇數訊號量,一個偶數訊號量,都初始化為1
- 先用掉偶數的訊號量,因為要讓奇數先啟動,等奇數列印完再釋放
- 具體流程就是 第一次的時候先減掉偶數的訊號量 奇數執行緒列印完成以後用掉奇數的訊號量。然後釋放偶數的訊號量如此迴圈
- 具體流程就是 第一次的時候先減掉偶數的訊號量 奇數執行緒列印完成以後用掉奇數的訊號量。然後釋放偶數的訊號量如此迴圈
import java.util.concurrent.Semaphore; /** * @ClassName AlternatePrinting * @Author yunlogn * @Date 2019/5/21 * @Description 交替列印奇偶數 */ public class AlternatePrinting { static int i = 0; public static void main(String[] args) throws InterruptedException { Semaphore semaphoreOdd = new Semaphore(1); Semaphore semaphoreEven = new Semaphore(1); semaphoreOdd.acquire(); //讓奇數先等待啟動,所以先減掉偶數的訊號量 等奇數執行緒來釋放 SemaphorePrintEven semaphorePrintEven = new SemaphorePrintEven(semaphoreOdd, semaphoreEven); Thread t1 = new Thread(semaphorePrintEven); t1.start(); SemaphorePrintOdd semaphorePrintOdd = new SemaphorePrintOdd(semaphoreOdd, semaphoreEven); Thread t2 = new Thread(semaphorePrintOdd); t2.start(); } /** * 使用訊號量實現 */ static class SemaphorePrintOdd implements Runnable { private Semaphore semaphoreOdd; private Semaphore semaphoreEven; public SemaphorePrintOdd(Semaphore semaphoreOdd, Semaphore semaphoreEven) { this.semaphoreOdd = semaphoreOdd; this.semaphoreEven = semaphoreEven; } @Override public void run() { try { semaphoreOdd.acquire();//獲取訊號量 semaphoreOdd在初始化的時候被獲取了訊號量所以這裡被阻塞了,所以會先執行下面的奇數執行緒 while (true) { i++; if (i % 2 == 0) { System.out.println("偶數執行緒:" + i); semaphoreEven.release();//釋放偶數訊號量 讓奇數執行緒那邊的阻塞解除 //再次申請獲取偶數訊號量,因為之前已經獲取過,如果沒有奇數執行緒去釋放,那麼就會一直阻塞在這,等待奇數執行緒釋放 semaphoreOdd.acquire(); } } } catch (InterruptedException e) { e.printStackTrace(); } } } static class SemaphorePrintEven implements Runnable { private Semaphore semaphoreOdd; private Semaphore semaphoreEven; public SemaphorePrintEven(Semaphore semaphoreOdd, Semaphore semaphoreEven) { this.semaphoreOdd = semaphoreOdd; this.semaphoreEven = semaphoreEven; } @Override public void run() { try { semaphoreEven.acquire(); while (true) { i++; if (i % 2 == 1) { System.out.println("奇數執行緒:" + i); semaphoreOdd.release(); //釋放奇數訊號量 讓偶數執行緒那邊的阻塞解除 //這裡阻塞,等待偶數執行緒釋放訊號量 //再次申請獲取奇數訊號量,需要等偶數執行緒執行完然後釋放該訊號量,不然阻塞 semaphoreEven.acquire(); } } } catch (Exception ex) {} } } }
- 需要注意的是,如果某個執行緒來不及釋放就異常中斷了,會導致另一個執行緒一直在等,造成死鎖。 雖然這個異常不在這個問題的考慮範圍內 但是可以使用
finally
來包裹釋放鎖資源
同步鎖列印
- 讓兩個執行緒使用同一把鎖。交替執行 。
- 判斷是不是奇數 如果是奇數進入奇數執行緒執行列印並加一。然後執行緒釋放鎖資源。然後讓該執行緒等待
- 判斷是不是偶數,如果是偶數進入偶數執行緒執行列印並加一。然後執行緒釋放鎖資源。然後讓該執行緒等待
import java.util.concurrent.atomic.AtomicInteger; /** * @ClassName AlternatePrinting * @Author yunlogn * @Date 2019/5/21 * @Description 交替列印奇偶數 */ public class AlternatePrinting { public static AtomicInteger atomicInteger = new AtomicInteger(1); public static void main(String[] args) throws InterruptedException { Thread a=new Thread(new AThread()); Thread b=new Thread(new BThread()); a.start(); b.start(); } public static class AThread implements Runnable { @Override public void run() { while (true) { synchronized (atomicInteger) { if (atomicInteger.intValue() % 2 != 0) { System.out.println("奇數執行緒:" + atomicInteger.intValue()); atomicInteger.getAndIncrement(); // 奇數執行緒釋放鎖資源 atomicInteger.notify(); try { atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } else { try { // 奇數執行緒等待 atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } public static class BThread implements Runnable { @Override public void run() { while (true){ synchronized (atomicInteger){ if(atomicInteger.intValue() %2== 0 ){ System.out.println("偶數執行緒:"+ atomicInteger.intValue()); atomicInteger.getAndIncrement(); // 偶數執行緒釋放鎖資源 atomicInteger.notify(); try { atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } }else{ try { // 偶數執行緒等待 atomicInteger.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } } }
歡迎關注 http://yunlongn.github.io