一道面試題:面試
啓動兩個線程, 一個輸出 1,3,5,7…99, 另外一個輸出 2,4,6,8…100 最後 STDOUT 中按序輸出 1,2,3,4,5…100ide
錯誤實現1:this
public class NotifyErrorTest { private int i = 1; Thread t1 = new Thread(){ @Override public void run() { while (true) { synchronized (this) { notify(); if (i <= 100) { System.out.println(currentThread().getName() + ":" + i); i++; try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }; Thread t2 = new Thread(){ @Override public void run() { while (true) { synchronized (this) { notify(); if (i <= 100) { System.out.println(currentThread().getName() + ":" + i); i++; try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }; public static void main(String[] args){ NotifyErrorTest test = new NotifyErrorTest(); test.t1.start(); test.t2.start(); } }
結果:線程
Thread-0:1 Thread-1:1
打印出這兩個後,線程就一直被掛起了。爲何會這樣呢。
先不考慮這種難看的重複代碼需不須要重構,自己代碼就有問題,雖然看起來都用了this,可是其實兩個this所表示的含義不一樣,咱們兩個線程裏面加上以下代碼code
System.out.println(this.getClass());
會發現打印出對象
class pers.marscheng.thread.NotifyErrorTest$1 class pers.marscheng.thread.NotifyErrorTest$2
原來兩個this不是同一個對象,匿名類會生成新的對象,因此致使兩個線程獲取的monitor鎖是不一樣的。這就致使wait()方法調用以後,兩個線程都被掛起,可是再也沒人能把他們喚醒,並且因爲鎖不一樣,兩個線程都同時執行了,打印出的都是1。rem
正確實現:get
public class NotifyTest implements Runnable { int i = 1; public static void main(String[] args) { NotifyTest test = new NotifyTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start(); t2.start(); } @Override public void run() { while (true) { synchronized (this) { this.notify(); if (i <= 100) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + ":" + i); i++; try { this.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } }
經過condition實現:it
public class ConditionTest implements Runnable{ Lock lock = new ReentrantLock(); Condition condition = lock.newCondition(); int i = 1; @Override public void run() { try { lock.lock(); while (true) { condition.signal(); if (i <= 100) { System.out.println(Thread.currentThread().getName() + ":" + i); i++; try { condition.await(); } catch (InterruptedException e) { e.printStackTrace(); } } } } finally { lock.unlock(); } } public static void main(String[] args) { ConditionTest test = new ConditionTest(); Thread t1 = new Thread(test); Thread t2 = new Thread(test); t1.start(); t2.start(); } }
拓展:io
啓動三個線程, 一個輸出 1,4,7,10…100, 一個輸出 2,5,8,11…101,最後一個暑促3,6,9,12...102 最後 STDOUT 中按序輸出 1,2,3,4,5…102
實現:
public class NotifyTest2 implements Runnable { private Object prev; private Object self; AtomicInteger i; private NotifyTest2(AtomicInteger num,Object prev, Object self) { this.i = num; this.prev = prev; this.self = self; } @Override public void run() { while (true) { synchronized (prev) { synchronized (self) { if (i.get() <= 102) { System.out.println(Thread.currentThread().getName() + ":" + i.get()); i.getAndIncrement(); self.notify(); } } try { prev.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Object a = new Object(); Object b = new Object(); Object c = new Object(); AtomicInteger num = new AtomicInteger(1); NotifyTest2 testA = new NotifyTest2(num,c,a); NotifyTest2 testB = new NotifyTest2(num,a,b); NotifyTest2 testC = new NotifyTest2(num,b,c); new Thread(testA).start(); new Thread(testB).start(); new Thread(testC).start(); } }
利用AtomicInteger作爲共享變量。