——java多線程機制

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------

進程:

   是一個正在執行中的程序。

   沒一個進程執行都有一個執行順序。該順序是一個執行路徑,或者就一個控制單元。

   一個進程中至少有一個線程。

線程:

   就是進程中的一個獨立的控制單元。線程在控制着進程的執行。



java JVM虛擬機啓動時會有一個java.exe


   該進程中至少一個線程負責java程序的執行。而且這個線程運行的代碼存在與main方法中。

   該線程稱爲--主線程,名稱爲:main。

    擴展:

    其實java 虛擬機JVM啓動時是兩個線程:一個是主線程,另一個垃圾回收機制的線程。


多線程的運行狀態:

1,創建:線程被創建;

實例代碼:

Thread thread = new Thread();

2,運行:通過start方法運行線程,有運行資格和運行權。

實例代碼:

thread.start();

3,凍結:如果線程被sleep,或者wait,則進入凍結狀態。放棄了運行權。

實例代碼:

thread.sleep(12);或者是

thread.wait();

4,阻塞(臨時)狀態:線程只有執行資格,沒有執行權。當線程被喚醒

         或者sleep時間到的時候,可能會進入阻塞狀態,獲得執行資格,但沒有執行權。

5,消亡:線程調用了stop方法,或者run方法執行完畢之後,線程結束。

注:stop方法已經過時,JDK1.5之後採用了新的停止機制!

多線程獲取對象及名稱:

static Thread currentThread()獲得當前線程的對象。

String getName()獲得線程的方法:

默認是:Thread + 編號,編號從0開始。

void setName()設置線程的名稱。


線程的定義和啓動

創建線程的第一種方式:

     繼承Thread類。繼承方式

步驟:

1,定義類繼承Thread。

2,重寫Thread類的run方法。

目的:將自定義代碼存儲到run方法中,讓線程運行。

3,調用線程的start方法,該方法兩個作用:啓動線程,調用run方法。

   語法:

class 類名 extends Thread{

@Override

public void run(){

//線程執行的代碼

}

}

創建線程的第二種方式:實現方式

步驟:

1,定義類實現Runnable接口;

2,重寫Runnable接口中的run方法;

  作用:將線程要運行的代碼存放到run方法中。

3,通過Thread類建立線程對象;

4,將Runnable接口的子類對象作爲參數傳遞給Thread類的構造函數。

  重寫run方法的目的:

因爲:自定義的run方法所屬的對象是Runnable接口的子類對象

所以要讓線程去執行指定對象的run方法,就必須明確該run方法所屬的對象。

5,調用Thread類的start方法開啓線程,並調用Runnable接口接口子類的run方法。

   語法:

class 類名 implements Runnable{

@Override

public void run(){

//線程執行的代碼

}

}   

   實現方式和繼承方式有什麼區別?

實現方式,避免了java中單繼承的侷限性。

在定義線程時,建議使用實現方式。

   兩種方式的區別:

繼承Thread類:線程代碼存放在Thread類的run方法中,具有單重繼承的侷限性

實現Runnable,線程代碼存放在Runnable接口的實現類run方法中,沒有侷限性,更加靈活


  執行機制:

    在多個線程啓動後,每一次的運行結果都不同。因爲對個線程都在獲取CPU的執行權,

    CPU執行到誰,誰就執行。需要明確的是:CPU在某一時刻,只能有一個程序在執行(多核除外)

    CPU在做着快速切換,以達到看上去是同時運行的效果。

    可以形象的把多線程的運行行爲看成是在互相搶奪CPU的執行權。

    這就是多線程的一個特性:隨機性,誰搶到誰執行,至於執行多長時間,有CPU決定。

   爲什麼要重寫run方法?

Thread類用於描述線程,該類就定義了一個功能,用於存儲線程要運行的代碼。

該功能就是run方法。即,Thread的類中的run方法,用於存儲線程要運行的代碼。


   run方法和start方法的區別:

1,run方法時用於存儲線程要執行的代碼,

  如果直接執行run方法,則此時是主線程在執行run方法。

  此時是單線程,會先執行完run方法中的代碼,纔去執行其他的代碼。

2,start方法是用於啓動線程,並執行run方法。此時run方法就由start方法開啓的線程執行。

  此時就是主線程和start方法啓動的線程在輪流執行。是多線程。

  當線程啓動之後,就不能再用同一線程重新啓動, 會報IllegalThreadStateException非法線程狀態異常。

自定義線程的示例代碼:

package com.itheima.threaddemos;

/**
 * 自定義線程類
 * @author wuyong
 *
 */
public class MyThreadTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		
		//創建用方式一自定義線程
		MyThread mt1 = new MyThread();
		//創建方式二定義的線程
		Thread mt2 = new Thread(new MyThread2());
		
		//調用start方法,來啓動線程,和調用run方法
		mt1.start();
		mt2.start();
		
		for (int i = 0; i < 30; i++) {
			System.out.println("系統線程 --" + i + "-- runnig ");
		}	
	}

}
/**
 * 自定義線程類
 * 實現方式:繼承Thread類
 * @author wuyong
 *
 */
class MyThread extends Thread{
	/**
	 * 重寫Thread類的run方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("繼承Thread類的自定義線程1 --" + i + "-- runnig ");
		}
	}
}
/**
 * 自定義線程類
 * 實現方式:實現Runnable接口。
 * @author wuyong
 *
 */
class MyThread2 implements Runnable{
	/**
	 * 重寫Thread類的run方法
	 */
	@Override
	public void run() {
		for (int i = 0; i < 30; i++) {
			System.out.println("實現Runnable接口的自定義線程2 --" + i + "-- runnig ");
		}
	}
}


線程的等待和喚醒:

wait(),notify(),notifyAll()用來操作線程爲什麼定義在Object類中

  1,這些方法存在於同步中。

2,使用這些方法時必須要標識所屬的同步鎖。

3,鎖可以是任意對象,所有任意對象調用的方法一定是定義在Object中。

   wait(),sleep()有什麼區別?

wait():釋放資源,釋放鎖。

sleep():釋放資源,不釋放鎖。

wait();//使線程等待

notify();//喚醒線程,通常是喚醒線程池中的第一個被等待的線程。使用notify方法容易只喚醒本方線程,而導致所有線程都等待。

notifyAll();//喚醒線程池中的所有線程。

這些方法一般都使用在同步中,因爲要對持有監視器(鎖)的線程操作。

所以都要使用在同步中,因爲只有同步才具有鎖。



爲什麼這些操作線程的方法都要定義在Object類彙總呢?

因爲這些方法在操作同步線程時,都必須要標識他們所操作線程持有的鎖,

只有同一個鎖上的被等待線程,可以被同一個鎖notify喚醒。不可以對不同鎖中的線程進行喚醒。也就是說,等待和喚醒必須是同一個鎖。

       而鎖可以是任意對象,所以任意對象調用的方法定義在Object類中。

注:當有多個線程同時操作資源時,會導致程序出現錯誤。因爲沒有判斷標記。

要用while循環判斷標記,和使用notifyAll方法喚醒等待中的線程來判斷標記。

等待喚醒的示例代碼:  

package com.itheima.threaddemos.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 線程間通信測試,及其wait,notify,notifyAll方法的測試類
 * @author wuyong
 *
 */
public class ThreadCommunicationTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resorce2 res2 = Resorce2.getInstance();
		
		InputThread2 in = new InputThread2(res2);
		OutputThread2 out = new OutputThread2(res2);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(in);
		Thread t3 = new Thread(out);
		Thread t4 = new Thread(out);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
/**
 * 資源類
 * @author wuyong
 *
 */
class Resorce2{
	private static final Resorce2 res2 = new Resorce2();
	private Resorce2(){};
	public static Resorce2 getInstance(){return res2;}
	//姓名
	private String name;
	//國家
	private String country;
	//狀態標示,用於控制線程是否等待或者喚醒
	private boolean flag;
	
	private Lock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();
	private Condition con_cus = lock.newCondition();
	/**
	 * 設置資源類的屬性,並切換線程的等待或者喚醒,同步方法
	 * @param name
	 * @param country
	 */
	public synchronized void set(String name,String country) {
//		if (flag) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.country = country;
		System.out.println("現在出場的球星是---" + this.name);
		flag = true;
//		this.notify();
		this.notifyAll();
	}
	public void set2(String name,String country) {
		lock.lock();
		try {
			while (flag)
				con_pro.await();
			this.name = name;
			this.country = country;
			System.out.println("現在出場的球星是---" + this.name);
			flag = true;
			con_cus.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 打印出資源的信息,同步方法
	 */
	public synchronized void out() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name + "---" + this.country);
		flag = false;
		this.notifyAll();
	}
	public void out2() {
		lock.lock();
		try {
			while (!flag)
				con_cus.await();
			System.out.println(this.name + "---" + this.country);
			flag = false;
			con_pro.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
}
/**
 * 設置資源信息線程類
 * @author wuyong
 *
 */
class InputThread2 implements Runnable{
	private Resorce2 res2;
	public InputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		int i = 0;
		while (true) {
			if (i == 0) 
				res2.set2("kobe", "Amarican");
			else
				res2.set2("姚明", "中國");
			i = (i + 1) % 2;
		}
	}
}

/**
 * 輸出資源信息線程類
 * @author wuyong
 *
 */
class OutputThread2 implements Runnable{
	private Resorce2 res2;
	public OutputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		while (true) {
			res2.out2();
		}
	}
}

在JDK1.5中,新的等待喚醒機制:

  將synchronized替換成了Lock接口操作。

  將Object中的wait,notify,notifyAll,替換成了Condition對象,

  該對象可以lock所,進行獲取。

  注:一個Lock對象可以有多個Condition實例,可以用對應的Condition實例,操作對應的線程。

  其實:相當於就是用Lock代替了synchronized的操作,而Condition寫的與是代替了Object中的wait,notify,notifyAll。

  示例代碼:

package com.itheima.threaddemos;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * JDK1.5之後的新的等待和喚醒機制
 * @author wuyong
 *
 */
public class Resource
{
    private String name ;
    private int count = 1 ;
    private boolean flag = false ;
    
    //線程鎖
    private Lock lock = new ReentrantLock();
    //Condition實例對象,控制線程的等待和喚醒
    private Condition condition_pro = lock. newCondition();
    private Condition condition_con = lock. newCondition();


    /**
     * 生產
     * @param name
     * @throws InterruptedException
     */
    public  void produce( String name)throws InterruptedException
    {
        lock .lock();
         try
         {
             while(flag )
                condition_pro .await(); //生產線程等待
             this.name = name+ "--"+count ++;


            System .out. println(Thread .currentThread().getName()+"...生產者.." +this.name);
            flag = true ;
            condition_con .signal();//消費線程喚醒
            condition_con .signalAll();//所有消費線程喚醒
            
         }
         finally
         {
            lock .unlock(); //釋放鎖的動作一定要執行。
         }
    }
    
    /**
     * 消費
     * @throws InterruptedException
     */
    public  void consume() throws InterruptedException
    {
        lock .lock();//獲取鎖
         try
         {
             while(!flag )
                condition_con .await();//消費線程等待
            System .out. println(Thread .currentThread().getName()+"...消費者........." +this.name);
            flag = false ;
            condition_pro .signal();//生產線程喚醒
            condition_pro .signalAll();//所有生產線程喚醒
         }
         finally
         {
            lock .unlock();//釋放鎖
         }
    }
}



線程的停止:

由於Thread類中的stop方法已經過時,stop方法偶安全問題,所有不建議使用stop方法來停止。

    如何停止線程呢?

    只有一種方法,就是讓run方法結束,

    開啓多線程是,運行代碼通常使用循環結構,所以控制了循環,就可以使run方法結束,即線程結束。

   
    特殊情況:

        當線程處於了凍結狀態:如:wait,sleep,join。

        就不會讀取到標記。那麼線程就不會結束。


   解決方案:強制清除線程的凍結狀態,獲得執行資格。     

    當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時就需要對凍結進行清除。

   強制讓線程恢復到運行狀態中來,這樣就可以操作標記,讓線程結束。

    而Thread類提供了該方法 interrupt();

    interrupt()方法時強制將處於凍結狀態下的線程恢復到運行狀態。

結束線程的實例代碼:

package com.itheima.threaddemos.ThreadCommunication;

/**
 * 線程停止測試類
 * 條件:必須是多線程下,的同步代碼。
 * 由於Thread類中的stop方法已經過時,
 * 如何停止線程呢?
 * 只有一種方法,就是讓run方法結束,
 * 開啓多線程是,運行代碼通常使用循環結構,所以控制了循環,就可以使run方法結束,即線程結束。
 * 
 * 特殊情況:
 *   當線程處於了凍結狀態:如:wait,sleep,join。
 *   就不會讀取到標記。那麼線程就不會結束。
 *   
 * 當沒有指定的方式讓凍結的線程恢復到運行狀態時,這時就需要對凍結進行清除。
 * 強制讓線程恢復到運行狀態中來,這樣就可以操作標記,讓線程結束。
 * 而Thread類提供了該方法 interrupt();
 * @author wuyong
 *
 */
public class ThreadStopTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		
		MyStopThread mst = new MyStopThread();
		
		Thread t1= new Thread(mst);
		Thread t2 = new Thread(mst);
		
		t1.setDaemon(true);
		t2.setDaemon(true);
		t1.start();
		t2.start();
		int i = 0;
		while(true){
			if (i == 60) { 
//				強制將凍結狀態下的線程恢復到運行狀態,使線程對標記進行判斷,從而到達控制循環,以結束run方法。
				t1.interrupt();
				t2.interrupt();
				break;
			}
			System.out.println(Thread.currentThread().getName() + "---run");
			i++;
		}
	}

}
/**
 * 自定義線程類
 * @author wuyong
 *
 */
class MyStopThread implements Runnable{
	//控制線程狀態的標記
	boolean flag = true;
	@Override
	public synchronized void run() {
		while (flag) {
			try {
				this.wait();//線程等待(凍結狀態)
			} catch (InterruptedException e) {
				flag = false;
				System.out.println(Thread.currentThread().getName() + "stop");
			}
			System.out.println(Thread.currentThread().getName() + "...run");
		}
	}
	
}


多線程的安全問題。

   問題原因:

當多條語句在操作同一個線程共享數據時,一個線程對多條語句只執行了一部分,還沒有執行完,

另一個線程參與進來執行。導致共享數據的錯誤。

   解決辦法:

對多條操作共享數據的語句,只能讓一個線程都執行完,在執行過程中其他線程不能參與執行。

   java對於線程安全問題提供了專業的解決方式。

    方式一:同步代碼塊

   synchronized(對象)//這稱爲鎖(同步鎖)。必須要是同一對象。持有鎖的線程纔可以在同步中執行,

      //沒有鎖的線程即使獲得CPU執行權也無法執行。例如火車的衛生間。 

    {

    //需要被同步的代碼

  }

    方式二:同步方法。

    訪問修飾符 sychronized 返回值類型 方法名(參數列表){

//方法體

    }

   注:同步函數中,使用的鎖是this。即函數所屬對象的引用。

而靜態的同步方法,使用的鎖是該方法所屬在類的字節碼文件對象。

因爲:

  靜態進入內存時,內存中還沒有本類對象,但是一定有該類對應的字節碼文件對象。

  類名.class 該對象的類型是Class。

    靜態同步方法的語法格式:

    訪問修飾符 static sychronized 返回值類型 方法名(方法所屬類名.class){

//方法體

    }

   如何確定同步那些地方:

       1,明確那些代碼是對線程運行代碼。

       2,明確那些是共享數據。(一般是成員變量)

       3,明確多線程運行代碼中哪些語句是操作共享數據的。

   同步的前提:

1,要有多個線程。

2,多個線程共同使用一個所,就是共享同一資源。

3,同步中只能有一個線程在執行。

   同步的好處:解決了多線程的安全問題。

    弊端:多個線程執行時,每次都要對所進行判斷,較爲消耗資源。

多線程同步的示例代碼:

package com.itheima.threaddemos.synchronizeds;

/**
 * 同步代碼塊測試
 * @author wuyong
 *
 */
public class SynchronizedTest {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//創建自定義線程類對象
		MyThread mt = new MyThread();
		//創建四個線程
		Thread t = new Thread(mt);
		Thread t1 = new Thread(mt);
		Thread t2 = new Thread(mt);
		Thread t3 = new Thread(mt);
		//啓動四個線程
		t.start();
		t1.start();
		t2.start();
		t3.start();
	}

}
/**
 * 自定義線程類,實現方式爲,實現Runnable接口
 * @author wuyong
 *
 */
class MyThread implements Runnable{
	//票數
	private int tickets = 400;
	Object obj = new Object();
	/**
	 * 實現Runnable中的run方法,將線程執行的代碼保存至run方法中
	 */
	@Override
	public void run() {
		// TODO Auto-generated method stub
		while (true) {
			//同步代碼塊
			synchronized (obj) {//此處的對象參數 必須保證是同一個,否則同步失敗
				if (tickets > 0) {
					//讓線程休眠,此時如果不同步就會出現線程安全問題,
					try {
						Thread.sleep(10);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
					//讓票數遞減
					System.out.println(Thread.currentThread().getName()
							+ ":ticket//" + tickets--);
				} else break; //退出循環
			}
		}
	}
	
}



多線程的綜合示例:

生產者和消費者的模擬

package com.itheima.threaddemos;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 生產者和消費者的模擬
 * 思路:
 * 	1,提供一個商品類,其中定義一個商品生產的狀態標標識;
 * 	2,自定義一個生產者的線程類;
 * 	3,自定義一個消費者的線程類;
 * 	4,在自定義線程類的同步代碼中,循環判斷商品的生產狀態。
 * @author wuyong
 *
 */
public class Pro_CusDemo {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		//創建一個商品對象
		Goods goods = new Goods();
		
		//創建生產線程
		Pro_Thread pro = new Pro_Thread(goods);
		//創建消費線程
		Cus_Thread cus = new Cus_Thread(goods);
		
		//創建4個線程
		Thread tp1 = new Thread(pro);
		Thread tp2 = new Thread(pro);
		Thread tc1 = new Thread(cus);
		Thread tc2 = new Thread(cus);
		
		//開啓線程,開始生產和消費
		tp1.start();
		tp2.start();
		tc1.start();
		tc2.start();
	}

}

/**
 * 商品類
 * @author wuyong
 *
 */
class Goods{
	//商品名稱
	private String name;
	//商品數量
	private int counts;
	//商品生產狀態
	private boolean flag;
	
	//JDK1.5後的鎖對象
	Lock lock = new ReentrantLock();
	
	//生產者的Condition對象
	Condition pro_con = lock.newCondition();
	//消費者的Condition對象
	Condition cus_con = lock.newCondition();
	
	/**
	 * 生產的方法
	 * JDK1.5之前的方式,用同步來實現
	 * @param name
	 */
	public synchronized void produce(String name){
			while (flag) {//循環判斷標識
				try {
					wait();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			counts++;
			this.name = name + counts;
			System.out.println(Thread.currentThread().getName() + "生產了++++++" + this.name);
			flag = true;
			notifyAll();
	}
	/**
	 * 消費的方法
	 * JDK1.5之前的方式,用同步來實現
	 * @param name
	 */
	public synchronized void customer(){
			while (!flag) {//必須要循環判斷標識!!!!
				try {
					wait();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + "消費了" + name);
			flag = false;
			notifyAll();
	}
	/**
	 *  生產的方法
	 *	JDK1.5 中提供的多線程升級解決方案。
	 *	將同步Synchronized替換成現實Lock操作。
	 *	將Object中的wait,notify notifyAll,替換了Condition對象。
	 *	該對象可以Lock鎖 進行獲取。
	 *	該示例中,實現了本方只喚醒對方操作。

	 *	Lock:替代了Synchronized
     *	lock  //獲得鎖
     *	unlock //釋放鎖,這一步必須要執行
     *	newCondition()  //獲得Condition對象
     *
	 *	Condition:替代了Object wait notify notifyAll
     *	await();  //線程等待
     *	signal();	//喚醒單個線程
     *	signalAll();//喚醒所有線程
     *
	 * @param name
	 */
	public void produce2(String name){
		try {
			lock.lock();
			while (flag) {
				try {
					pro_con.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			counts++;
			this.name = name + counts;
			System.out.println(Thread.currentThread().getName() + "生產了++++++" + this.name);
			flag = true;
			cus_con.signal();
		} finally {
			lock.unlock();
		}
	}
	/**
	 * 消費的方法
	 * JDK1.5 中提供的多線程升級解決方案。
	 */
	public void customer2(){
		try {
			lock.lock();
			while (!flag) {//必須要循環判斷標識!!!!
				try {
					cus_con.await();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
			System.out.println(Thread.currentThread().getName() + "消費了" + name);
			flag = false;
			pro_con.signal();
		} finally{
			lock.unlock();
		}
}
}
/**
 * 生產者
 * @author wuyong
 *
 */
class Pro_Thread implements Runnable{
	private Goods goods;
	
	public Pro_Thread(Goods goods){ 
		this.goods = goods;
	}

	@Override
	public void run() {
		while (true) {
			//JDK1.5之前的等待喚醒機制
			goods.produce("金克拉");
			//JDK1.5之後的等待喚醒機制
//			goods.produce2("金克拉");
		}
	}
	
}
/**
 * 消費者
 * @author wuyong
 *
 */
class Cus_Thread implements Runnable{
	
	private Goods goods;

	public Cus_Thread(Goods goods){ 
		this.goods = goods;
	}
	@Override
	public void run() {
		while (true) {
			//JDK1.5之前的等待喚醒機制
			goods.customer();
			//JDK1.5之後的等待喚醒機制
//			goods.customer2();
		}
	}	
}

---------------------- <a href="http://www.itheima.com"target="blank">ASP.Net+Unity開發</a>、<a href="http://www.itheima.com"target="blank">.Net培訓</a>、期待與您交流! ----------------------

wait(),notify(),notifyAll()用來操作線程爲什麼定義在Object類中

  1,這些方法存在於同步中。

2,使用這些方法時必須要標識所屬的同步鎖。

3,鎖可以是任意對象,所有任意對象調用的方法一定是定義在Object中。

   wait(),sleep()有什麼區別?

wait():釋放資源,釋放鎖。

sleep():釋放資源,不釋放鎖。

wait();//使線程等待

notify();//喚醒線程,通常是喚醒線程池中的第一個被等待的線程。使用notify方法容易只喚醒本方線程,而導致所有線程都等待。

notifyAll();//喚醒線程池中的所有線程。

等待喚醒的示例代碼:  

package com.itheima.threaddemos.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 線程間通信測試,及其wait,notify,notifyAll方法的測試類
 * @author wuyong
 *
 */
public class ThreadCommunicationTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resorce2 res2 = Resorce2.getInstance();
		
		InputThread2 in = new InputThread2(res2);
		OutputThread2 out = new OutputThread2(res2);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(in);
		Thread t3 = new Thread(out);
		Thread t4 = new Thread(out);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
/**
 * 資源類
 * @author wuyong
 *
 */
class Resorce2{
	private static final Resorce2 res2 = new Resorce2();
	private Resorce2(){};
	public static Resorce2 getInstance(){return res2;}
	//姓名
	private String name;
	//國家
	private String country;
	//狀態標示,用於控制線程是否等待或者喚醒
	private boolean flag;
	
	private Lock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();
	private Condition con_cus = lock.newCondition();
	/**
	 * 設置資源類的屬性,並切換線程的等待或者喚醒,同步方法
	 * @param name
	 * @param country
	 */
	public synchronized void set(String name,String country) {
//		if (flag) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.country = country;
		System.out.println("現在出場的球星是---" + this.name);
		flag = true;
//		this.notify();
		this.notifyAll();
	}
	public void set2(String name,String country) {
		lock.lock();
		try {
			while (flag)
				con_pro.await();
			this.name = name;
			this.country = country;
			System.out.println("現在出場的球星是---" + this.name);
			flag = true;
			con_cus.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 打印出資源的信息,同步方法
	 */
	public synchronized void out() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name + "---" + this.country);
		flag = false;
		this.notifyAll();
	}
	public void out2() {
		lock.lock();
		try {
			while (!flag)
				con_cus.await();
			System.out.println(this.name + "---" + this.country);
			flag = false;
			con_pro.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
}
/**
 * 設置資源信息線程類
 * @author wuyong
 *
 */
class InputThread2 implements Runnable{
	private Resorce2 res2;
	public InputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		int i = 0;
		while (true) {
			if (i == 0) 
				res2.set2("kobe", "Amarican");
			else
				res2.set2("姚明", "中國");
			i = (i + 1) % 2;
		}
	}
}

/**
 * 輸出資源信息線程類
 * @author wuyong
 *
 */
class OutputThread2 implements Runnable{
	private Resorce2 res2;
	public OutputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		while (true) {
			res2.out2();
		}
	}
}


這些方法一般都使用在同步中,因爲要對持有監視器(鎖)的線程操作。

所以都要使用在同步中,因爲只有同步才具有鎖。



爲什麼這些操作線程的方法都要定義在Object類彙總呢?

因爲這些方法在操作同步線程時,都必須要標識他們所操作線程持有的鎖,

只有同一個鎖上的被等待線程,可以被同一個鎖notify喚醒。不可以對不同鎖中的線程進行喚醒。也就是說,等待和喚醒必須是同一個鎖。

       而鎖可以是任意對象,所以任意對象調用的方法定義在Object類中。

注:當有多個線程同時操作資源時,會導致程序出現錯誤。因爲沒有判斷標記。

要用while循環判斷標記,和使用notifyAll方法喚醒等待中的線程來判斷標記。

等待喚醒的示例代碼:  

package com.itheima.threaddemos.ThreadCommunication;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * 線程間通信測試,及其wait,notify,notifyAll方法的測試類
 * @author wuyong
 *
 */
public class ThreadCommunicationTest2 {

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		// TODO Auto-generated method stub
		Resorce2 res2 = Resorce2.getInstance();
		
		InputThread2 in = new InputThread2(res2);
		OutputThread2 out = new OutputThread2(res2);
		
		Thread t1 = new Thread(in);
		Thread t2 = new Thread(in);
		Thread t3 = new Thread(out);
		Thread t4 = new Thread(out);
		
		t1.start();
		t2.start();
		t3.start();
		t4.start();
	}

}
/**
 * 資源類
 * @author wuyong
 *
 */
class Resorce2{
	private static final Resorce2 res2 = new Resorce2();
	private Resorce2(){};
	public static Resorce2 getInstance(){return res2;}
	//姓名
	private String name;
	//國家
	private String country;
	//狀態標示,用於控制線程是否等待或者喚醒
	private boolean flag;
	
	private Lock lock = new ReentrantLock();
	private Condition con_pro = lock.newCondition();
	private Condition con_cus = lock.newCondition();
	/**
	 * 設置資源類的屬性,並切換線程的等待或者喚醒,同步方法
	 * @param name
	 * @param country
	 */
	public synchronized void set(String name,String country) {
//		if (flag) {
		while (flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		this.name = name;
		this.country = country;
		System.out.println("現在出場的球星是---" + this.name);
		flag = true;
//		this.notify();
		this.notifyAll();
	}
	public void set2(String name,String country) {
		lock.lock();
		try {
			while (flag)
				con_pro.await();
			this.name = name;
			this.country = country;
			System.out.println("現在出場的球星是---" + this.name);
			flag = true;
			con_cus.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
	
	/**
	 * 打印出資源的信息,同步方法
	 */
	public synchronized void out() {
		while (!flag) {
			try {
				this.wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println(this.name + "---" + this.country);
		flag = false;
		this.notifyAll();
	}
	public void out2() {
		lock.lock();
		try {
			while (!flag)
				con_cus.await();
			System.out.println(this.name + "---" + this.country);
			flag = false;
			con_pro.signal();
		} catch (InterruptedException e) {
			
		} finally {
			lock.unlock();
		}
	}
}
/**
 * 設置資源信息線程類
 * @author wuyong
 *
 */
class InputThread2 implements Runnable{
	private Resorce2 res2;
	public InputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		int i = 0;
		while (true) {
			if (i == 0) 
				res2.set2("kobe", "Amarican");
			else
				res2.set2("姚明", "中國");
			i = (i + 1) % 2;
		}
	}
}

/**
 * 輸出資源信息線程類
 * @author wuyong
 *
 */
class OutputThread2 implements Runnable{
	private Resorce2 res2;
	public OutputThread2(Resorce2 res2){
		this.res2 = res2;
	}
	@Override
	public void run() {
		while (true) {
			res2.out2();
		}
	}
}

發佈了25 篇原創文章 · 獲贊 4 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章