LeetCode刷題之旅【多線程篇-3】中等: 1115. 交替打印FooBar

2019-11-20 

目錄

題目:

解題1

解題2:對象鎖

解題3:信號量


題目:

 

解題1


class FooBar {
	private int n;
	private static Object lock = new Object();
	private static volatile Boolean flag = true;

	public FooBar(int n) {
		this.n = n;
	}

	public void foo(Runnable printFoo) throws InterruptedException {

		for (int i = 0; i < n; i++) {
			synchronized (lock){
				if (!flag){
					lock.wait();
				}
				// printBar.run() outputs "bar". Do not change or remove this line.
				printFoo.run();
				flag = false;
				lock.notify();
			}
		}
	}

	public void bar(Runnable printBar) throws InterruptedException {

		for (int i = 0; i < n; i++) {
			synchronized (lock){
				if (flag){
					lock.wait();
				}
				// printBar.run() outputs "bar". Do not change or remove this line.
				printBar.run();
				flag = true;
				lock.notify();
			}
		}
	}

	public static void main(String[] args) {
		FooBar fooBar = new FooBar(2);
		Runnable runnable1 = new Runnable() {
			@Override
			public void run() {
				try {
					fooBar.foo(new Runnable() {
						@Override
						public void run() {
							System.out.print("foo");
						}
					});
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Runnable runnable2 =new Runnable() {
			@Override
			public void run() {
				try {
					fooBar.bar(new Runnable() {
						@Override
						public void run() {
							System.out.print("bar");
						}
					});
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		};
		Thread thread1 = new Thread(runnable1);
		thread1.start();
		Thread thread2 = new Thread(runnable2);
		thread2.start();

	}
}
  • 對象同步鎖:確保foo和bar方法只會被一個線程執行
  • 線程休眠與喚醒:wait/notify
//對象同步鎖用法推薦
synchronized (obj) {
              while (&lt;condition does not hold&gt;)
                  obj.wait();
              ... // Perform action appropriate to condition
          }

解題2:對象鎖

分析: 將printFoo操作和printBar操作分別加鎖,來保證兩者串行發生,設置一個boolean類型變量fooTurn來指明當前操作的輪次,當其爲True時,打印foo,當其爲False時,打印bar。每個線程在同步代碼塊內部,判斷當前輪次和自己的操作是否符合,若符合,則執行打印操作,若不符合,則放棄鎖。

class FooBar {
    private int n;
    private boolean fooTurn = true;
    private Object lock = new Object();
    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            
            synchronized(lock) {
                if (!fooTurn) lock.wait();
                fooTurn = false;
                // printFoo.run() outputs "foo". Do not change or remove this line.
                printFoo.run();
                lock.notifyAll();
            }
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {
        
        for (int i = 0; i < n; i++) {
            
            synchronized(lock) {
                if (fooTurn) lock.wait();
                fooTurn = true;
                // printBar.run() outputs "bar". Do not change or remove this line.
        	    printBar.run();
                lock.notifyAll();
            }
        }
    }
}

作者:copyreadmachine
鏈接:https://leetcode-cn.com/problems/print-foobar-alternately/solution/java-suo-by-copyreadmachine/
來源:力扣(LeetCode)
著作權歸作者所有。商業轉載請聯繫作者獲得授權,非商業轉載請註明出處。

 

解題3:信號量

  • 使用信號量,acquire 就是 值+1
    release 就是 值-1
    簡單理解就是當值是0,就可以繼續執行,不爲零就等待變爲0後再執行
public class FooBar {
    private int n;
    //here is the full path, or maybe cann't compile in leetcode.
    java.util.concurrent.Semaphore semaphoreFoo=new java.util.concurrent.Semaphore(0);
    java.util.concurrent.Semaphore semaphoreBar=new java.util.concurrent.Semaphore(0);

    public FooBar(int n) {
        this.n = n;
    }

    public void foo(Runnable printFoo) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            printFoo.run();
            //由於下面阻塞了,所以這裏變爲0,下面的方法就能繼續執行
            semaphoreBar.release();
            //這裏讓他等一會,等到bar()執行完
            semaphoreFoo.acquire();
        }
    }

    public void bar(Runnable printBar) throws InterruptedException {

        for (int i = 0; i < n; i++) {
            // 進來先變爲1,就會等上面的release()使他變爲0,才進行,所以肯定在foo之後。
            semaphoreBar.acquire();
            printBar.run();
            //bar()執行完了,就讓foo()繼續。
            semaphoreFoo.release();
        }
    }
}

  • Java 併發包中的信號量 Semaphore 實際上是一個功能完畢的計數信號量,從概念上講,它維護了一個許可集
    合,對控制一定資源的消費與回收有着很重要的意義。Semaphore 可以控制某個資源被同時訪問的任務數,它
    通過acquire()獲取一個許可,release()釋放一個許可。如果被同時訪問的任務數已滿,則其他 acquire 的任務
    進入等待狀態,直到有一個任務被 release 掉,它才能得到許可。
  • 就是 Semaphore 僅僅是對資源的併發訪問的任務數進行監控,而不會保證線程安全,因此,在訪問的時候,要自己控制線程的安全訪問。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章