Java線程_Notify,NotifyAll,Wait方法

       怎麼辦,好幾天沒寫博客了,心裏感覺不踏實。水一篇吧,水水更健康吐舌頭。在看Java線程這本書的電子版,看到第四章notify、wait、notifyAll這幾個方法,前面的notify和wait還好,比較簡單,就是需要注意的是notify和wait方法必須放在同步代碼中。可是爲什麼要這樣呢?原因是如果不將notify和wait放到同步代碼中的話,他們之間可能會產生競態條件。現設有兩個線程,如果不將notify和wait放在同步代碼中可能發生如下情況:

       一、第一個線程檢查條件,確定需要等待。
       二、第二個線程設定條件。
       三、第二個線程調用notify()方法,本次調用因爲沒有其他的等待線程而直接返回。
       四、第一個線程調用wait()。

       附上一段關於notify和wait的Android代碼:

package com.wly.testwait_notify;


import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.app.Activity;
import android.view.Menu;
import android.view.View;
import android.widget.Button;

public class MainActivity extends Activity {

	int count = 0;
	Button b;
	CountThread countThread;
	Handler handler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			super.handleMessage(msg);
			b.setText(msg.arg1 + "");
		}
	};
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		b = new Button(this);
		b.setText("0");
		this.setContentView(b);
		
		countThread = new CountThread();
		countThread.start();
		
		b.setOnClickListener(new View.OnClickListener() {
			
			@Override
			public void onClick(View v) {
				if(countThread.isAlive()) {
					countThread.wakeup();
				}
			}
		});
	}

	class CountThread extends Thread {

		@Override
		public void run() {
			super.run();
			while(count < 20) {
				sendMsg();
				try {
					sleep(500);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
		//發送通知,並進入等待狀態,注意wait()只能位於synchronized代碼塊中
		public synchronized void sendMsg() {
			Message msg = new Message();
			msg.arg1 = count ++;
			handler.sendMessage(msg);
			if(count % 5 == 0) {
				try {
					System.out.println("--等待10秒--");
					wait(10000);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
		/**
		 * 喚醒線程,注意notify()只能位於synchronized代碼塊中
		 */
		public synchronized void wakeup() {
			notify();
		}
	}
}

      說出來有點好笑的是,筆者以前想在Android中用wait()結果因爲沒有放到同步代碼塊中,導致一運行就報錯,還以爲Android中不能用wait呢!咳咳,,不要笑啦,進入今天正題NotifyAll。可能是最近辦公室比較吵得原因吧(搶票的搶票,聊天的聊天),使得筆者靜不下心來,於是一個notifyAll盯着看了好久纔看懂。

      還是一樣分析,爲什麼要有notifyAll,他和notify有什麼區別嗎?notifyAll用來喚醒一個對象上的多個等待線程。就是說首先這個對象有多個線程等待着使用該對象,其次就是要喚醒所有的線程,讓他們去競爭對象上的鎖。這裏需要注意一下的是:雖然所有該對象上的等待線程都被喚醒了,但他們還需要獲得對象上的鎖才能向下運行。於是當調用notifyAll後還是隻有那個獲取到了鎖的線程才能繼續往下運行。好了,一段notifyAll的測試代碼:

package com.wly.javathread.chap4;

/**
 * 測試notifyAll方法
 * 
 * @author wly
 * 
 */
public class TestNotifyAll implements Runnable {

	static ResourcePool mPool;
	
	public static void main(String[] args) {
		
		ResourcePool pool = new ResourcePool();
		
		System.out.println("總資源數:" + pool.resource_max);

		TestNotifyAll tn_1 = new TestNotifyAll(pool);
		TestNotifyAll tn_2 = new TestNotifyAll(pool);
		TestNotifyAll tn_3 = new TestNotifyAll(pool);
		TestNotifyAll tn_4 = new TestNotifyAll(pool);
		
		new Thread(tn_1).start();
		new Thread(tn_2).start();
		new Thread(tn_3).start();
		new Thread(tn_4).start();
		
	}
	
	@Override
	public void run() {
		while(true) {
			mPool.getResource(2);
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			mPool.freeResource(1);
		}
	}

	public TestNotifyAll(ResourcePool pool) {
		this.mPool = pool;
	}
	
	static class ResourcePool {
		int resource_max = 10; //資源最大數量
		int resource_count = 0; //當前佔用資源數量
		
		/**
		 * 獲取資源
		 * @param num
		 */
		public synchronized void getResource(int num) {
			if((resource_count + num) <= resource_max) { //有可用資源,領取使用
				resource_count += num;
				System.out.println("線程" + Thread.currentThread().getId() + "拿到2個資源");
				System.out.println("剩餘資源數:" + (resource_max -resource_count));
			} else { //無可用資源,等
				try {
					System.out.println("資源數量不夠,線程" + Thread.currentThread().getId() + ":進入等待狀態");
					wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}
		
		/**
		 * 釋放資源
		 * @param num
		 */
		public synchronized void freeResource(int num) {
			if(num <= (resource_count)) {
				resource_count -= num;
				System.out.println("釋放" + num + "個資源,當前剩餘資源" + (resource_max-resource_count));
				notifyAll();
			} 
		}
	}
}

        運行結果如下:

總資源數:10
線程9拿到2個資源
剩餘資源數:8
線程11拿到2個資源
剩餘資源數:6
線程8拿到2個資源
剩餘資源數:4
線程10拿到2個資源
剩餘資源數:2
釋放1個資源,當前剩餘資源3
線程9拿到2個資源
剩餘資源數:1
釋放1個資源,當前剩餘資源2
線程11拿到2個資源
剩餘資源數:0
釋放1個資源,當前剩餘資源1
資源數量不夠,線程8:進入等待狀態
釋放1個資源,當前剩餘資源2
線程10拿到2個資源
剩餘資源數:0
釋放1個資源,當前剩餘資源1
資源數量不夠,線程9:進入等待狀態
釋放1個資源,當前剩餘資源2
線程11拿到2個資源
剩餘資源數:0
釋放1個資源,當前剩餘資源1
資源數量不夠,線程8:進入等待狀態
釋放1個資源,當前剩餘資源2
線程10拿到2個資源
剩餘資源數:0
釋放1個資源,當前剩餘資源1
資源數量不夠,線程11:進入等待狀態
釋放1個資源,當前剩餘資源2
線程9拿到2個資源
剩餘資源數:0
釋放1個資源,當前剩餘資源1
資源數量不夠,線程8:進入等待狀態
釋放1個資源,當前剩餘資源2
線程10拿到2個資源
剩餘資源數:0

       可能運行結果需要仔細看才能看出流程,主要就是釋放資源時通知所有等待線程,若此時某個等待線程搶到了鎖會進行資源請求,或者進入等待狀態(資源不夠時)。

      O啦~~~

      轉載請保留出處:http://blog.csdn.net/u011638883/article/details/18322537

      謝謝!!

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