2019-11-20
目錄
題目:
解題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 (<condition does not hold>)
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 僅僅是對資源的併發訪問的任務數進行監控,而不會保證線程安全,因此,在訪問的時候,要自己控制線程的安全訪問。