---------------------- <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();
}
}
}