Java多線程-線程的交互


一、線程交互的基礎知識
void notify():喚醒在此對象監視器上等待的單個線程。
void notifyAll():喚醒在此對象監視器上等待的所有線程。
void wait():導致當前的線程等待,直到其他線程調用此對象的 notify()方法或 notifyAll()方法。

當然,wait()還有另外兩個重載方法:
void wait(long timeout):導致當前的線程等待,直到其他線程調用此對象的 notify()方法或 notifyAll()方法,或者超過指定的時間量。
void wait(long timeout, int nanos):導致當前的線程等待,直到其他線程調用此對象的 notify()方法或 notifyAll()方法,或者其他某個線程中斷當前線程,或者已超過某個實際時間量。

關於等待/通知,要記住的關鍵點是:
必須從同步代碼塊內調用wait()、notify()、notifyAll()方法。線程不能調用對象上等待或通知的方法,除非它擁有那個對象的鎖。

wait()、notify()、notifyAll()都是Object的實例方法。與每個對象具有鎖一樣,每個對象可以有一個線程列表,他們等待來自該信號(通知)。線程通過執行對象上的wait()方法獲得這個等待列表。從那時候起,它不再執行任何其他指令,直到調用對象的notify()方法為止。如果多個線程在同一個對象上等待,則將只選擇一個線程(不保證以何種順序)繼續執行。如果沒有線程等待,則不采取任何特殊操作。

例子

package cn.thread;

/**
 * 計算1+2+3 ... +100的和
 * 
 * @author 林計欽
 * @version 1.0 2013-7-23 上午10:06:04
 */
public class ThreadSum extends Thread {
    int total = 0;

    @Override
    public void run() {

        synchronized (this) {
            for (int i = 0; i < 101; i++) {
                total += i;
            }
            //(完成計算了)喚醒在此對象監視器上等待的單個線程,在本例中線程ThreadInteractionTest被喚醒
            notify();
        }

    }
}
package cn.thread;

/**
 * 線程的交互
 * 
 * @author 林計欽
 * @version 1.0 2013-7-23 上午10:04:11
 */
public class ThreadInteractionTest {
    public static void main(String[] args) {
        
        ThreadSum sum = new ThreadSum();
        // 啟動計算線程
        sum.start();
        // 線程ThreadInteractionTest擁有sum對象上的鎖。
        // 線程為了調用wait()或notify()方法,該線程ThreadInteractionTest必須是那個對象鎖的擁有者
        synchronized (sum) {
            try {
                System.out.println("等待對象sum完成計算。。。");
                // 當前線程ThreadInteractionTest等待
                sum.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sum對象計算的總和是:" + sum.total);
        }
        
    }
}
等待對象sum完成計算。。。
sum對象計算的總和是:5050

注意:當在對象上調用wait()方法時,執行該代碼的線程立即放棄它在對象上的鎖。然而調用notify()時,並不意味着這時線程會放棄其鎖。如果線程仍然在完成同步代碼,則線程在移出之前不會放棄鎖。因此,只要調用notify()並不意味着這時該鎖變得可用。

二、多個線程在等待一個對象鎖時使用notifyAll()
在多數情況下,最好通知等待某個對象的所有線程。如果這樣做,可以在對象上使用notifyAll()讓所有在此對象上等待的線程沖出等待區,返回到可運行狀態。

package cn.thread;

/**
 * 計算1+2+3 ... +100的和
 * 
 * @author 林計欽
 * @version 1.0 2013-7-23 上午10:06:04
 */
public class ThreadSum2 extends Thread {
    int total = 0;

    @Override
    public void run() {

        synchronized (this) {
            for (int i = 0; i < 101; i++) {
                total += i;
            }
            //通知所有在此對象上等待的線程 
            notifyAll();
        }

    }
}
package cn.thread;


/**
 * 線程的交互
 * 
 * @author 林計欽
 * @version 1.0 2013-7-23 上午10:04:11
 */
public class ThreadInteractionTest2 extends Thread{
    ThreadSum2 sum;
    
    public ThreadInteractionTest2(ThreadSum2 sum){
        this.sum=sum;
    }
    
    @Override
    public void run() {
        synchronized (sum) {
            try {
                System.out.println("等待對象sum完成計算。。。");
                // 當前線程ThreadInteractionTest等待
                sum.wait();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("sum對象計算的總和是:" + sum.total);
        }
    }
    
    public static void main(String[] args) {
        ThreadSum2 sum = new ThreadSum2();
        
        //啟動三個線程,分別獲取計算結果 
        new ThreadInteractionTest2(sum).start();
        new ThreadInteractionTest2(sum).start();
        new ThreadInteractionTest2(sum).start();
        
        // 啟動計算線程
        sum.start();
        
        
    }
}
等待對象sum完成計算。。。
等待對象sum完成計算。。。
等待對象sum完成計算。。。
sum對象計算的總和是:5050
sum對象計算的總和是:5050
sum對象計算的總和是:5050

談一下synchronized和wait()、notify()等的關系:
1、有synchronized的地方不一定有wait,notify
2、有wait,notify的地方必有synchronized.這是因為wait和notify不是屬於線程類,而是每一個對象都具有的方法,而且,這兩個方法都和對象鎖有關,有鎖的地方,必有synchronized。
另外,注意一點:如果要把notify和wait方法放在一起用的話,必須先調用notify后調用wait,因為如果調用完wait,該線程就已經不是currentthread了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM