1. 程式人生 > >java.lang.Thread.sleep()方法和java.lang.Object.wait()方法之間的區別

java.lang.Thread.sleep()方法和java.lang.Object.wait()方法之間的區別

java.lang.Thread.sleep():

sleep()方法為Thread類定義的靜態方法,因此需要通過Thread類呼叫該方法:

Thread.sleep(1000);

Javadoc對該方法的描述如下:

Causes the currently executing thread to sleep (temporarily cease execution) for the specified number of milliseconds, subject to the precision and accuracy of system timers and schedulers.The thread does not lose ownership of any monitors

.

呼叫sleep()方法將會導致當前正在執行執行緒休眠特定的時間

在Java位元組碼層面,獲取鎖的位元組碼指令為:monitorenter,釋放鎖的位元組碼指令為:monitorexit,所以我個人認為doc裡的monitors可以理解為物件鎖

也就是說,呼叫sleep()方法後進入休眠狀態的執行緒並不會釋放其持有的物件鎖

package com.sean;

public class Test implements Runnable {

	@Override
	public void run(){
		this.s();
	}
	
	public synchronized void s(){
		System.out.println(Thread.currentThread().getName()
				+ " get lock.");
		try {
			Thread.sleep(1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		System.out.println(Thread.currentThread().getName()
				+ " release lock.");
	} 
	
public static void main(String[] args){
		Test st = new Test();
		Thread t1 = new Thread(st, "t1");
		Thread t2 = new Thread(st, "t2");
		t1.start();	
		t2.start();
	}
}

執行結果如下(t2雖然已經啟動,但是直到t1釋放鎖後才能開始執行):

t1 get lock.
t1 release lock.
t2 get lock.
t2 release lock.

java.lang.Object.wait():

wait()方法是Object類定義的普通方法,因此任何物件都可呼叫該方法

this.wait(1000);

Javadoc對wait()方法的描述如下:

Causes the current thread to wait until either another thread invokes the java.lang.Object.notify() method or the java.lang.Object.notifyAll() method for this object, or a specified amount of time has elapsed.

The current thread must own this object's monitor.

This method causes the current thread (call it T) to place itself in the wait set for this object and then to relinquish any and all synchronization claims on this object. Thread T becomes disabled for thread scheduling purposes and lies dormant until one of four things happens:

  • Some other thread invokes the notify method for this object and thread T happens to bearbitrarily chosen as the thread to be awakened. (參見notify()方法的Javadoc:If any threads are waiting on this object, one of them is chosen to be awakened. The choice is arbitrary and occurs at the discretion of the implementation)
  • Some other thread invokes the notifyAll method for this object.
  • Some other thread interrupts thread T.
  • The specified amount of real time has elapsed, more or less. If timeout is zero, however, then real time is not taken into consideration and the thread simply waits until notified.

The thread T is then removed from the wait set for this object and re-enabled for thread scheduling. It then competes in the usual manner with other threads for the right to synchronize on the object; once it has gained control of the object, all its synchronization claims on the object are restored to the status quo ante - that is, to the situation as of the time that the wait method was invoked. Thread T then returns from the invocation of the wait method. Thus, on return from the wait method, the synchronization state of the object and of thread T is exactly as it was when the wait method was invoked.

A thread can also wake up without being notified, interrupted, or timing out, a so-calledspurious wakeup. While this will rarely occur in practice, applications must guard against it by testing for the condition that should have caused the thread to be awakened, and continuing to wait if the condition is not satisfied. In other words, waits should always occur in loops, like this one:

synchronized (obj) {
	while (<condition does not hold>)
        	obj.wait(timeout);
         	... // Perform action appropriate to condition
}

If the current thread is interrupted by any thread before or while it is waiting, then anInterruptedException is thrown. This exception is not thrown until the lock status of this object has been restored as described above.

Note that the wait method, as it places the current thread into the wait set for this object, unlocks only this object; any other objects on which the current thread may be synchronized remain locked while the thread waits. (這點要特別注意)

This method should only be called by a thread that is the owner of this object's monitor. A thread becomes the owner of the object's monitor in one of three ways(簡單來說就是獲取該物件的鎖):

  • By executing a synchronized instance method of that object(呼叫該物件的同步方法).
  • By executing the body of a synchronized statement that synchronizes on the object(進入該物件的同步程式碼塊).
  • For objects of type Class, by executing a synchronized static method of that class(呼叫該物件的同步靜態方法).

Only one thread at a time can own an object's monitor.

wait()方法、notify()方法和notifyAll()方法都需要先獲取到物件上的鎖後才能呼叫,否則會報java.lang.IllegalMonitorStateException

當執行緒呼叫物件的wait()方法後,執行緒將進入等待狀態並釋放其持有的該物件上的鎖(執行緒仍然持有其它物件的鎖)

當物件的notify()方法、notifyAll()方法被呼叫,或者執行緒被中斷,或者執行緒等待了wait()方法指定的等待時間(如果為wait(0),則只有物件的notify()方法或notifyAll()方法被呼叫後執行緒才會退出等待狀態)後,執行緒將退出等待狀態,並和其它執行緒公平的競爭物件上的鎖,一旦執行緒成功獲取到物件鎖,執行緒將從wait()方法呼叫返回,物件的同步狀態和執行緒的同步狀態將和wait()方法調時一樣

package com.sean;
public class Test1 implements Runnable {
	private Object lock;
	
	public Test1(Object lock){
		this.lock = lock;
	}

	@Override
	public void run(){
		synchronized(lock){
			System.out.println(Thread.currentThread().getName()
					+ " get lock.");
			try {
				lock.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(Thread.currentThread().getName()
					+ " release lock.");
		}
	}
}
package com.sean;

public class Test2 implements Runnable {
	private Object lock;
	
	public Test2(Object lock){
		this.lock = lock;
	}

	@Override
	public void run() {
		try {
			Thread.sleep(2*1000);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		synchronized(lock){
			System.out.println(Thread.currentThread().getName()
					+ " get lock.");
			lock.notifyAll();
			System.out.println(Thread.currentThread().getName()
					+ " release lock.");
		}
		synchronized(lock){
			System.out.println(Thread.currentThread().getName()
					+ " get lock.");
			System.out.println(Thread.currentThread().getName()
					+ " release lock.");
		}
	}
	
	public static void main(String[] args){
		Object lock = new Object();
		Thread t1 = new Thread(new Test1(lock), "t1");
		Thread t2 = new Thread(new Test2(lock), "t2");
		t1.start();
		t2.start();
	}
}

執行結果如下:

t1 get lock.
t2 get lock.
t2 release lock.
t2 get lock.
t2 release lock.
t1 release lock.