线程挂起和唤醒,Object 的 wait 和 sinal

wait() 和 notify() 顾名思义一个是等待,一个是唤醒。分别表示线程的挂起和唤醒。
两者都是Object类的方法。

wait() 使用的时候必须注意在 synchronized 的代码块里执行。

因为执行 wait 函数之前会先获取到目标对象的监视器,然后释放监视器,方便其他等待在这个目标对象上的线程可以继续执行下去。

package demo1;

public class SimpleWN {
	
	final static Object object = new Object(); //synchronized的目标对象,就是锁
	
	public static class T1 extends Thread {

		public void run() {
			synchronized(object) {
				System.out.println(System.currentTimeMillis()+": T1 start!");
				try {
					System.out.println(System.currentTimeMillis()+": T1 wait for object!");
					object.wait(); //线程挂起,停止执行,并且会把锁释放
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(System.currentTimeMillis()+": T1 end!");
				
			}
		}
		
	}
	
	public static class T2 extends Thread {

		public void run() {
			synchronized(object) {

				System.out.println(System.currentTimeMillis()+": T2 start! notify one thread");
				object.notify(); //随机唤醒一个等待在object对象上的线程。
				System.out.println(System.currentTimeMillis()+": T2 end!");
				
				try {
					Thread.sleep(2000);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				
			}
		}
		
	}
	
	public static void main(String[] args) throws InterruptedException {
		Thread t1 = new T1();
//		Thread t11 = new T1();
		
		Thread t2 = new T2();
		t1.start();
//		t11.start();
		
		Thread.sleep(2000);
		t2.start();
	}
	
}

和notify() 类似的有一个 notifyAll() 函数。

两者有些区别:

  • notify() 会有jvm随机选择一个线程唤醒,这个线程等待锁后就可以执行,其他线程仍旧挂起,除非继续唤醒

  • notifyAll() 会唤醒所有等待在这个锁上的线程,这些线程只要拿到锁后就可以执行

suspend() 和 resume() 是 Thread 类提供的一对用于线程的挂起和唤醒的api。

不过这对api已经被标明不建议使用,原因在于 suspend 挂起线程的时候,线程不会释放任何的锁。如果这个线程不被唤醒,那么将永远的占有锁,导致等待这个锁的其他线程全部执行不下去。

和 wait 的区别:

  • wait() 一定要在 synchronized 代码块中使用,因为要获得锁并且释放; suspend 不需要在synchronized 代码块中使用,也不会释放线程占有的锁。
  • wait 挂起的线程状态为 WAITING,suspend 挂起的线程状态为 RUNNABLE。这个线程状态对于查找问题的时候是会有误导的。

下面是使用的例子,并且模拟了 suspend 不释放锁的弊端

package demo1;

public class BadSuspend {
	
	public static Object object = new Object();
	
	
	
	public static class ChangeObjectThread extends Thread {
		
		public ChangeObjectThread(String name) {
			super.setName(name);
		}
		
		@Override
		public void run() {

			
			synchronized(object) {
				System.out.println("in " + getName());
				Thread.currentThread().suspend();  //挂起当前线程,但是不会释放锁 object
			}
			
		}
		
		
	}
	
	static ChangeObjectThread t1 = new ChangeObjectThread("t1");
	static ChangeObjectThread t2 = new ChangeObjectThread("t2");
	
	
	public static void main(String[] args) throws InterruptedException {
		t1.start();
		Thread.sleep(100);
		t2.start();
		
		t1.resume(); //唤醒 t1 线程,这个线程 resume 执行的顺序一般在 suspend 后面,因为上面 sleep 了 100 ms.但是这也不是绝对的。两个线程同步执行顺序不可控
		t2.resume(); //唤醒 t2 线程, 因为两个线程同步执行,resume 很有可能在线程里的 suspend 之前执行。导致线程错过被唤醒,而 object 锁被一直占用。假设有其它线程等待在这个锁上,那么将永远无法执行
		
		Thread.sleep(1000);
		
		System.out.println(t1.getState().toString());
		System.out.println(t2.getState().toString());
		
		t1.join();
		t2.join();
	}
	
	
}

输出结果如下,并且程序无法结束,因为程序挂起后错过了被唤醒的时机

in t1
in t2
TERMINATED
RUNNABLE
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章