一個執行緒通知另外一個執行緒結束
阿新 • • 發佈:2018-12-30
有一道面試題, 有一個集合,一個執行緒t1往裡面加元素,當集合的size為5的時候,讓t2執行緒結束;
三種實現方法:
方法一: 使用volatile
package thread; import java.util.ArrayList; import java.util.List; /** * Created by ZWZS on 2018/4/3. * 有一個list,一個執行緒往裡面新增元素,當list.size()長度為5的時候,通知執行緒2,讓執行緒2結束 * 第一種方式,使用volatile來實現,使用volatile時,執行緒t2,一直處於while(true),比較消耗效能 * 可以採用 wait()和notify()優化 */ public class NotifyStop { private volatile static List<String> list = new ArrayList<>(); public void add(String s) { list.add(s); System.out.println(Thread.currentThread().getName() + " " + s); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } public void get() { while (true) { if (list.size() == 5) { System.out.println("t2結束"); return; } } } public static void main(String[] args) { NotifyStop notifyStop = new NotifyStop(); new Thread(() -> { for (int i = 0; i < 10; i++) { notifyStop.add("abc"); } }, "t1").start(); new Thread(() -> { notifyStop.get(); }, "t2").start(); } }
使用voltaile時,執行緒t2處於while(true)迴圈中,比較消耗效能,可以讓集合的size()等於5的時候再執行.引申出第二種方法;
第二種方法: 使用wait()和notify();
package thread; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * Created by ZWZS on 2018/4/3. * 有一個list,一個執行緒往裡面新增元素,當list.size()長度為5的時候,通知執行緒2,讓執行緒2結束 * 第二種方式,使用wait()和notify()來優化 * 可以採用 wait()和notify()優化 */ public class NotifyStop2 { private static List<String> list = new ArrayList<>(); private final Object lock = new Object(); public void add(String s) { synchronized (lock) { list.add(s); System.out.println(Thread.currentThread().getName() + " " + s); try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) { lock.notify(); try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } public void get() { synchronized (lock) { if (list.size() != 5) { try { lock.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("t2結束"); lock.notify(); } } public static void main(String[] args) { NotifyStop2 notifyStop2 = new NotifyStop2(); //特別注意使用同一個物件進行呼叫 new Thread(() -> { for (int i = 0; i < 10; i++) { notifyStop2.add("abc"); } }, "t1").start(); new Thread(() -> { notifyStop2.get(); }, "t2").start(); } }
第三種方法: 使用CountdownLatch,執行緒2首先await(),當countdownLatch執行一次countdown時,執行緒2就執行一次.
package thread; import java.util.ArrayList; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; /** * Created by ZWZS on 2018/4/3. * 有一個list,一個執行緒往裡面新增元素,當list.size()長度為5的時候,通知執行緒2,讓執行緒2結束 * 第三種方式,使用countdownLatch來搞定 */ public class NotifyStop3 { private static List<String> list = new ArrayList<>(); private final CountDownLatch countDownLatch = new CountDownLatch(1); public void add(String s) { list.add(s); System.out.println(Thread.currentThread().getName() + " " + s); if (list.size() == 5) { countDownLatch.countDown(); } try { TimeUnit.MILLISECONDS.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } public void get() { try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } if (list.size() == 5) { System.out.println("t2收到通知"); } } public static void main(String[] args) { NotifyStop3 notifyStop3 = new NotifyStop3(); //特別注意使用同一個物件進行呼叫 new Thread(() -> { for (int i = 0; i < 10; i++) { notifyStop3.add("abc"); } }, "t1").start(); new Thread(() -> { notifyStop3.get(); }, "t2").start(); } }
使用wait()和notify()時,要注意wait()會釋放鎖,而notify()不會釋放鎖,推薦使用countdownLatch.