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線程將其喚醒。