大概的問題是這樣的:
有A,B,C三個線程, A線程輸出A, B線程輸出B, C線程輸出C
要求, 同時啟動三個線程, 按順序輸出ABC, 循環10次
這是一個多線程協同的問題, 本身多線程是沒有執行順序的, 順序不一定, Java在concurrent里面提供了多線程同步的支持
使用ReentrantLock來解決, 還有個state整數用來判斷輪到誰執行了
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 public class ABC { 5 private static Lock lock = new ReentrantLock();//通過JDK5中的鎖來保證線程的訪問的互斥 6 private static int state = 0; 7 8 static class ThreadA extends Thread { 9 @Override 10 public void run() { 11 for (int i = 0; i < 10;) { 12 lock.lock(); 13 if (state % 3 == 0) { 14 System.out.print("A"); 15 state++; 16 i++; 17 } 18 lock.unlock(); 19 } 20 } 21 } 22 23 static class ThreadB extends Thread { 24 @Override 25 public void run() { 26 for (int i = 0; i < 10;) { 27 lock.lock(); 28 if (state % 3 == 1) { 29 System.out.print("B"); 30 state++; 31 i++; 32 } 33 lock.unlock(); 34 } 35 } 36 } 37 38 static class ThreadC extends Thread { 39 @Override 40 public void run() { 41 for (int i = 0; i < 10;) { 42 lock.lock(); 43 if (state % 3 == 2) { 44 System.out.print("C"); 45 state++; 46 i++; 47 } 48 lock.unlock(); 49 } 50 } 51 } 52 53 public static void main(String[] args) { 54 new ThreadA().start(); 55 new ThreadB().start(); 56 new ThreadC().start(); 57 } 58 59 }
使用lock來保證只有一個線程在輸出操作, 要保證了state不會被兩個線程同時修改, 思路簡單
還可以使用condition, condition的效率可能會更高一些, await會釋放lock鎖, condition的await和signal與object的wait和notify方法作用類似
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 public class ABC2 { 6 private static Lock lock = new ReentrantLock(); 7 private static int count = 0; 8 private static Condition A = lock.newCondition(); 9 private static Condition B = lock.newCondition(); 10 private static Condition C = lock.newCondition(); 11 12 static class ThreadA extends Thread { 13 14 @Override 15 public void run() { 16 lock.lock(); 17 try { 18 for (int i = 0; i < 10; i++) { 19 while (count % 3 != 0) 20 A.await(); // 會釋放lock鎖 21 System.out.print("A"); 22 count++; 23 B.signal(); // 喚醒相應線程 24 } 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } finally { 28 lock.unlock(); 29 } 30 } 31 32 } 33 34 static class ThreadB extends Thread { 35 36 @Override 37 public void run() { 38 lock.lock(); 39 try { 40 for (int i = 0; i < 10; i++) { 41 while (count % 3 != 1) 42 B.await(); 43 System.out.print("B"); 44 count++; 45 C.signal(); 46 } 47 } catch (InterruptedException e) { 48 e.printStackTrace(); 49 } finally { 50 lock.unlock(); 51 } 52 } 53 54 } 55 56 static class ThreadC extends Thread { 57 58 @Override 59 public void run() { 60 lock.lock(); 61 try { 62 for (int i = 0; i < 10; i++) { 63 while (count % 3 != 2) 64 C.await(); 65 System.out.println("C"); 66 count++; 67 A.signal(); 68 } 69 } catch (InterruptedException e) { 70 e.printStackTrace(); 71 } finally { 72 lock.unlock(); 73 } 74 } 75 76 } 77 78 public static void main(String[] args) throws InterruptedException { 79 new ThreadA().start(); 80 new ThreadB().start(); 81 ThreadC threadC = new ThreadC(); 82 threadC.start(); 83 threadC.join(); 84 System.out.println(count); 85 } 86 }
使用信號量也可以, 這個思路最簡單, 整個代碼也比較簡潔
1 import java.util.concurrent.Semaphore; 2 3 public class ABC3 { 4 private static Semaphore A = new Semaphore(1); 5 private static Semaphore B = new Semaphore(1); 6 private static Semaphore C = new Semaphore(1); 7 8 static class ThreadA extends Thread { 9 10 @Override 11 public void run() { 12 try { 13 for (int i = 0; i < 10; i++) { 14 A.acquire(); 15 System.out.print("A"); 16 B.release(); 17 } 18 } catch (InterruptedException e) { 19 e.printStackTrace(); 20 } 21 } 22 23 } 24 25 static class ThreadB extends Thread { 26 27 @Override 28 public void run() { 29 try { 30 for (int i = 0; i < 10; i++) { 31 B.acquire(); 32 System.out.print("B"); 33 C.release(); 34 } 35 } catch (InterruptedException e) { 36 e.printStackTrace(); 37 } 38 } 39 40 } 41 42 static class ThreadC extends Thread { 43 44 @Override 45 public void run() { 46 try { 47 for (int i = 0; i < 10; i++) { 48 C.acquire(); 49 System.out.println("C"); 50 A.release(); 51 } 52 } catch (InterruptedException e) { 53 e.printStackTrace(); 54 } 55 } 56 57 } 58 59 public static void main(String[] args) throws InterruptedException { 60 B.acquire(); C.acquire(); // 開始只有A可以獲取, BC都不可以獲取, 保證了A最先執行 61 new ThreadA().start(); 62 new ThreadB().start(); 63 new ThreadC().start(); 64 } 65 }
注意:
lock是需要lock所有者去釋放的, 即誰lock, 誰釋放, 不可以跨線程, 會報java.lang.IllegalMonitorStateException;
semaphore是沒有所有者的說法, 可以跨線程釋放和獲取.
這是一道java筆試題, 多線程的問題現在越來越多的出現在筆試中, 要好好學習.
水平有限, 如有錯漏, 請指針, 歡迎拍磚, 共同探討!
參考文獻:
- 深入淺出Java Concurrency: http://www.blogjava.net/xylz/archive/2010/07/08/325587.html