例子
例1 最基礎的等待-通知
下面一個例子,一個線程"waiting"在同步代碼塊調用了Object#wait()方法,另一個線程"timedWaiting"調用了Object#wait(3000)等待3000ms,主線程sleep 5000ms后喚醒所有線程。
import java.util.concurrent.CompletableFuture; import java.util.concurrent.TimeUnit; import lombok.extern.slf4j.Slf4j; /** * @see Object#wait(long) 等待對應的毫秒數(不為0)或者被喚醒,執行后續代碼 * @see Object#wait() 只有被喚醒(底層源碼調用wait 0 ms)才能執行后續代碼 * @author rhyme * @date 2020/5/30 11:38 */ @Slf4j public class ObjectWaitMain { private static final Object lock = new Object(); private static final Runnable waiting = () -> { synchronized (lock) { try { log.info("Thread: {}, will Object#wait()", Thread.currentThread().getName()); lock.wait(); log.info("Thread: {} is notified.", Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }; private static final Runnable timedWaiting = () -> { synchronized (lock) { try { log.info( "Thread: {}, will be notified or wait 3000 milliseconds.", Thread.currentThread().getName()); lock.wait(3000); log.info( "Thread: {}, after being notified or wait 3000 milliseconds.", Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } } }; public static void main(String[] args) throws InterruptedException { CompletableFuture.allOf( CompletableFuture.runAsync(waiting), CompletableFuture.runAsync(timedWaiting)); // 主線程sleep5000ms,當3000ms后"timedWaiting"線程執行"wait(3000)"后的代碼塊 // 如果"timedWaiting"線程在3000ms被notify,那么會立即執行后續代碼,不會wait 3000ms TimeUnit.MILLISECONDS.sleep(5000); synchronized (lock) { log.info("main will notifyAll waiting thread."); lock.notifyAll(); } log.info("main end."); } }
執行結果:
例2 Object#wait(long)的參數大於0與等於0
測試類代碼
/** * @author rhyme * @date 2020/5/31 0:43 */ @Slf4j public class ThreadPoolExecutorTest { private ThreadPoolExecutor threadPoolExecutor; @Before public void initializeThreadPoolExecutor() { threadPoolExecutor = new ThreadPoolExecutor(4, 8, 10, TimeUnit.SECONDS, new LinkedBlockingDeque<>(4)); } @After public void teardown() throws InterruptedException { threadPoolExecutor.shutdown(); TimeUnit.SECONDS.sleep(2); threadPoolExecutor = null; }
大於0
/** * 當 {@link Object#wait(long)}的參數是大於0, 線程wait對象的毫秒數后, 不需被喚醒, 就可再次獲取鎖執行wait后續代碼 * @see Object#wait(long) */ @Test public void synchronizedTimedWaitingTest() { Runnable timedWaiting = () -> { synchronized (this) { while (true) { try { log.info("before Object#wait(1);"); this.wait(1); log.info("after Object#wait(1);"); } catch (InterruptedException e) { e.printStackTrace(); } } } }; threadPoolExecutor.execute(timedWaiting); }
執行結果:
等於0或Object#wait()
/** * 當 {@link Object#wait(long)}的參數是0, 線程只能是被喚醒后, 才能再次獲取鎖執行wait后續代碼 * @see Object#wait() */ @Test public void synchronizedWaitingTest() { Runnable timedWaiting = () -> { synchronized (this) { while (true) { try { log.info("before Object#wait(0);"); // 與"this.wait()"等價 this.wait(0); log.info("after Object#wait(0);"); } catch (InterruptedException e) { e.printStackTrace(); } } } }; threadPoolExecutor.execute(timedWaiting); }
執行結果, 該線程一直wait, 線程池關閉結束, UT結束:
總結
Object#wait(long) 等待對應的毫秒數后(不為0)或者在等待過程中被喚醒后,就能再次獲取鎖並且獲得時間片后執行wait后面代碼;
Object#wait() 只有被喚醒后(底層源碼調用wait 0 ms)才能再次獲取鎖執行wait后面代碼;
Thread#sleep或TimeUnit#sleep, 除非當前sleep的線程被interrupt, 否則必須sleep對應的時間才能執行后續代碼, 並且如果之前獲取到鎖, 不會釋放鎖.
Object#wait()方法源碼如下:
public final void wait() throws InterruptedException { wait(0); }
public final native void wait(long timeout) throws InterruptedException;