前言
文本已收錄至我的GitHub倉庫,歡迎Star:https://github.com/bin392328206/six-finger
種一棵樹最好的時間是十年前,其次是現在
我知道很多人不玩qq了,但是懷舊一下,歡迎加入六脈神劍Java菜鳥學習羣,羣聊號碼:549684836 鼓勵大家在技術的路上寫博客
絮叨
這個是一個很簡單的多線程問題,但是小六六一看覺得很簡單,但是真正寫起來也不是那麼簡單,說明小六六自己的代碼還是寫的太少了哈哈
題目
我們提供了一個類:
public class Foo {
public void one() { print("one"); }
public void two() { print("two"); }
public void three() { print("three"); }
}
三個不同的線程將會共用一個 Foo 實例。
- 線程 A 將會調用 one() 方法
- 線程 B 將會調用 two() 方法
- 線程 C 將會調用 three() 方法
請設計修改程序,以確保 two() 方法在 one() 方法之後被執行,three() 方法在 two() 方法之後被執行。
示例 1:
輸入: [1,2,3]
輸出: "onetwothree"
解釋:
有三個線程會被異步啓動。
輸入 [1,2,3] 表示線程 A 將會調用 one() 方法,線程 B 將會調用 two() 方法,線程 C 將會調用 three() 方法。
正確的輸出是 "onetwothree"。
題解一 synchronized 鎖和控制變量
package com.code.thread;
/**
* 上面方法就是採用一個synchronized wait notifyAll 這些來實現的,但是吧,還需要一個while去自旋,還得靠一個信號量,感覺有點low。
*/
class Foo {
private int flag=0;
private Object lock=new Object();
public Foo() {
}
public void first(Runnable printFirst) throws InterruptedException {
synchronized(lock){
while (flag!=0){
lock.wait();
}
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
flag=1;
lock.notifyAll();
}
}
public void second(Runnable printSecond) throws InterruptedException {
synchronized (lock) {
while (flag != 1) {
lock.wait();
}
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
flag = 2;
lock.notifyAll();
}
}
public void third(Runnable printThird) throws InterruptedException {
synchronized(lock) {
while (flag != 2) {
lock.wait();
}
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
lock.notifyAll();
}
}
public static void main(String[] args) throws Exception {
final Foo foo = new Foo();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.first(new Runnable() {
@Override
public void run() {
System.out.println(1);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.second(new Runnable() {
@Override
public void run() {
System.out.println(2);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.third(new Runnable() {
@Override
public void run() {
System.out.println(3);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t3.start();
t2.start();
t1.start();
}
}
上面方法就是採用一個synchronized wait notifyAll 這些來實現的,但是吧,還需要一個while去自旋,還得靠一個信號量,感覺有點low。
題解二 用CountDownLatch來控制
- countDownLatch這個類使一個線程等待其他線程各自執行完畢後再執行。
- 是通過一個計數器來實現的,計數器的初始值是線程的數量。每當一個線程執行完畢後,計數器的值就-1,當計數- 器的值爲0時,表示所有線程都執行完畢,然後在閉鎖上等待的線程就可以恢復工作了。
package com.code.thread;
import java.util.concurrent.CountDownLatch;
public class Foo1 {
//定義2個countDownLatch
private CountDownLatch countDownLatchA=new CountDownLatch(1); //說明只要一個線程調用它就放行 ,它是到0就放行
private CountDownLatch countDownLatchB=new CountDownLatch(1);
public Foo1() {
}
public void first(Runnable printFirst) throws InterruptedException {
// printFirst.run() outputs "first". Do not change or remove this line.
printFirst.run();
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
public static void main(String[] args) throws Exception {
final Foo1 foo = new Foo1();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.first(new Runnable() {
@Override
public void run() {
System.out.println(1);
}
});
foo.countDownLatchA.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.countDownLatchA.await();
foo.second(new Runnable() {
@Override
public void run() {
System.out.println(2);
}
});
foo.countDownLatchB.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.countDownLatchB.await();
foo.third(new Runnable() {
@Override
public void run() {
System.out.println(3);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t3.start();
t2.start();
t1.start();
}
}
方法三 信號量
基於信號量的解題思路
- Semaphore 是什麼?
- Semaphore 是一個計數信號量,必須由獲取它的線程釋放。
package com.code.thread;
import java.util.concurrent.Semaphore;
/**
*
* 基於信號量的解題思路
* Semaphore 是什麼?
* Semaphore 是一個計數信號量,必須由獲取它的線程釋放。
*
* 常用於限制可以訪問某些資源的線程數量,例如通過 Semaphore 限流。
*/
public class Foo2 {
//初始化Semaphore爲0的原因:
// 如果這個Semaphore爲零,如果另一線程調用(acquire)這個Semaphore就會產生阻塞,
// 便可以控制second和third線程的執行
private Semaphore spa=new Semaphore(0) ;
private Semaphore spb=new Semaphore(0);
public Foo2() {
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
spa.release();
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
spa.acquire();
printSecond.run();
spb.release();
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
spb.acquire();
printThird.run();
}
public static void main(String[] args) throws Exception {
final Foo2 foo = new Foo2();
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.first(new Runnable() {
@Override
public void run() {
System.out.println(1);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.second(new Runnable() {
@Override
public void run() {
System.out.println(2);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.third(new Runnable() {
@Override
public void run() {
System.out.println(3);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t3.start();
t2.start();
t1.start();
}
}
解題四 LockSupport
歸根結底,LockSupport調用的Unsafe中的native代碼:
- public native void unpark(Thread jthread);
- public native void park(boolean isAbsolute, long time);
- 兩個函數聲明清楚地說明了操作對象:park函數是將當前Thread阻塞,而unpark函數則是將另一個Thread喚醒。
package com.code.thread;
import java.util.concurrent.locks.LockSupport;
/**'
* 歸根結底,LockSupport調用的Unsafe中的native代碼:
* public native void unpark(Thread jthread);
* public native void park(boolean isAbsolute, long time);
* 兩個函數聲明清楚地說明了操作對象:park函數是將當前Thread阻塞,而unpark函數則是將另一個Thread喚醒。
*
* 與Object類的wait/notify機制相比,park/unpark有兩個優點:1. 以thread爲操作對象更符合阻塞線程的直觀定義;2. 操作更精準,可以準確地喚醒某一個線程(notify隨機喚醒一個線程,notifyAll喚醒所有等待的線程),增加了靈活性。
*/
public class Foo3 {
static Thread t1=null,t2=null,t3=null;
public Foo3() {
}
public void first(Runnable printFirst) throws InterruptedException {
printFirst.run();
}
public void second(Runnable printSecond) throws InterruptedException {
// printSecond.run() outputs "second". Do not change or remove this line.
printSecond.run();
}
public void third(Runnable printThird) throws InterruptedException {
// printThird.run() outputs "third". Do not change or remove this line.
printThird.run();
}
public static void main(String[] args) throws Exception {
final Foo3 foo = new Foo3();
t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
foo.first(new Runnable() {
@Override
public void run() {
System.out.println(1);
}
});
LockSupport.unpark(t2);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t2 = new Thread(new Runnable() {
@Override
public void run() {
LockSupport.park();
try {
foo.second(new Runnable() {
@Override
public void run() {
System.out.println(2);
}
});
LockSupport.unpark(t3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t3 = new Thread(new Runnable() {
@Override
public void run() {
LockSupport.park();
try {
foo.third(new Runnable() {
@Override
public void run() {
System.out.println(3);
}
});
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t3.start();
t2.start();
t1.start();
}
}
結尾
其實解法有很多,如果多線程不熟悉的小夥伴,可以去我的github博客,先熟悉一下。這題應該算簡單的題型了。
日常求贊
好了各位,以上就是這篇文章的全部內容了,能看到這裏的人呀,都是真粉。
創作不易,各位的支持和認可,就是我創作的最大動力,我們下篇文章見
六脈神劍 | 文 【原創】如果本篇博客有任何錯誤,請批評指教,不勝感激 !