Java多線程與併發應用-(4)-傳統線程通信技術試題

package com.lipeng;

public class LoopDemo {

	/**
	 * 線程A循環10次,然後線程B循環100次,然後A再循環10次,然後B再循環100次。如此循環50次。
	 * lipeng
	 * 2015-4-10
	 * @param args
	 */
	public static void main(String[] args) {
		MyTask task=new MyTask();
		RunA runA=new RunA(task);
		RunB runB=new RunB(task);
		new Thread(runA,"A").start();  //每個線程的start方法只能運行一次,但Run方法在不同的線程中可以運行多次。
		new Thread(runB,"B").start();
	}
}

class RunA implements Runnable
{
	private MyTask task;
	public RunA(MyTask task)
	{
		this.task=task;
	}
	@Override
	public void run() {
		try {
			for(int i=0;i<50;i++)
			{
				task.A();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}


class RunB implements Runnable
{
	private MyTask task;
	public  RunB(MyTask task)
	{
		this.task=task;
	}
	@Override
	public void run() {
		try {
			for(int i=0;i<50;i++)
			{
				task.B();
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
}

class MyTask
{
	private boolean aGo=true;//切換開關,當true時A執行,false時B執行,執行完後切換條件
	public synchronized void A()throws Exception{
		while(!aGo)  //爲何不用if???
		{
			//A 不能運行,等待
			this.wait();
		}
		for(int i=0;i<10;i++)
		{
			System.out.println("AAAAA--------------"+i);
		}
		//運行完之後,將aGo切換爲FALSE,讓B運行
		aGo=false;
		this.notify();//喚醒一個正在等待的線程 
	}
	
	public synchronized void B()throws Exception{
		while(aGo)
		{
			//B 不能運行,等待
			this.wait();
		}
		for(int i=0;i<100;i++)
		{
			System.out.println("B--------------"+i);
		}
		//運行完之後,將aGo切換爲true,讓A運行
		aGo=true;
		this.notify();//喚醒一個正在等待的線程   
	}
}


說明:

1. 如果有大於兩個多線程在執行,最好用notifyAll,爲什麼?

例如,如果本例中多起幾個線程,如果此時有A1,B1線程在wait,某個A線程執行完,將aGo置爲false,並喚醒正在等待鎖的其中一個線程,A1或B1,如果此時正好A1線程搶到了CPU,則A1運行,判斷方法A1進入wait狀態,而B1由於沒有被喚醒,之後繼續等待,那麼到最後,A1和B1都處於wait狀態,造成了死鎖。

而如果改爲notifyAll的話,A1和B1都被喚醒,搶到鎖的線程先執行,如果A1搶到的話,繼續等待,然後B1繼續執行,因爲此時aGo=false,所以B1執行,將aGo=true,A1繼續執行完畢。

結論:只要A和B啓動的線程的個數不同,就可能造成死鎖的情況。

   2. 判斷aGo的地方爲什麼要使用while,if不行嗎?在wait方法說明中,也推薦使用while,因爲在某些特定的情況下,線程有可能被假喚醒,使用while會循環檢測更穩妥。在本例中,A和B應該是交替運行的,但是如果多啓動幾個線程,如果有A1線程在等待(wait)狀態,而A2線程執行完畢,aGo設置爲false,喚醒正在等待的線程(可能是A也可能是B),如果是A被喚醒的話,如果用了if而不是while,那麼A執行,這樣A就連續執行了兩遍,不符合我們的設計要求,但如果換用while的話,即使A被喚醒,他會再次判斷aGo的值,發現是false,則繼續等待。直到B線程將其喚醒。

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