先介紹一種synchronized方式的實現:
提到交替打印,用到synchronized,不得不提到wait和notify,當前線程打印出當前數據之后,wait之前,需要通知其他,我即將wait,你可以繼續運行了,好了,話不多說,直接上代碼:
1 public class T02_ReentrantLock2 { 2 3 synchronized void s1() { 4 String abc = "abcdefghijklmnopqrstuvwxyz"; 5 String[] a = abc.split(""); 6 for(String value : a) { 7 System.out.println(value); 8 this.notify(); 9 try { 10 this.wait(); 11 Thread.sleep(100);// 防止打印速度過快導致混亂 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 } 16 17 } 18 synchronized void n1() { 19 20 for(int i = 1; i< 27; i++) { 21 System.out.println(i); 22 this.notify(); 23 try { 24 this.wait(); 25 Thread.sleep(100);// 防止打印速度過快導致混亂 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28 } 29 } 30 31 } 32 33 public static void main(String[] args) { 34 T02_ReentrantLock2 rl = new T02_ReentrantLock2(); 35 Thread t1 = new Thread(rl::s1); 36 Thread t2 = new Thread(rl::n1); 37 t1.start(); 38 t2.start(); 39 } 40 }
其實實現很簡單,最主要的點,就是什么時候wait,什么時候notify,正常情況下,他們是交替出現的,目的都是currentThread wait之前,喚起其他線程。
接下來是第二種,也是稍微復雜點的方式,直接使用ReentrantLock,進一步使用Condition,Condition相當於一個容器,在一個Lock中New多少個Condition,就相當於創建了多少個等待隊列,因此,通過一個lock,兩個不同的線程,兩個Condition,可以輕松實現以上需求,需要注意的是,每次lock后需要及時unlock。
public class T01_ReentrantLock1 { public static boolean isInteger(String str) { Pattern pattern = Pattern.compile("[0-9]*"); return pattern.matcher(str).matches(); } private static Lock lock = new ReentrantLock(); private static List<String> a = new ArrayList<String>( Arrays.asList("a", "1", "b", "2", "c", "3", "d", "4", "e", "5", "f", "6", "g", "7", "h", "8", "i", "9", "j", "10", "k", "11", "l", "12", "m", "13", "n", "14", "o", "15", "p", "16", "q", "17", "r", "18", "s", "19", "t", "20", "u", "21", "v", "22", "w", "23", "x", "24", "y", "25", "z", "26")); private static volatile int i = 0; public static void main(String[] args) { Condition number = lock.newCondition(); Condition abcString = lock.newCondition(); new Thread(() -> { try { Thread.sleep(100); } catch (InterruptedException e1) { e1.printStackTrace(); } while(i < a.size()){ lock.lock(); try { if (isInteger(a.get(i)) ) { abcString.await(); } if(i < a.size()) { System.out.println(Thread.currentThread() + " : " + a.get(i)); i++; number.signal(); } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }, "String").start(); new Thread(() -> { try { Thread.sleep(100); while (i < a.size()) { lock.lock(); // 必須帶條件才會相互切換,且判斷帶條件必須是同一個對象 if (!isInteger(a.get(i))) { number.await(); } // 必須在打印之前判斷下,是否另外一個線程i++后還小於數組,否則數組下表越界 if (i < a.size()) { System.out.println(Thread.currentThread() + " : " + a.get(i)); i++; abcString.signal(); } } } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } , "num").start(); } }
第二種方式,個人感覺還存在優化的空間,歡迎大家一起進行討論。