面試題-使用執行緒交替列印奇數偶數

不會敲程式碼的老王發表於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

相關文章