線程通信


講解等待喚醒機制之前,有必要搞清一個概念——

線程之間的通信:

多個線程在處理同一個資源,但是處理的動作(線程的任務)卻不相同。通過一定的手段使各個線程能有效的利用資源。而這種手段即—— 等待喚醒機制。


等待喚醒機制所涉及到的方法:


wait() :等待,將正在執行的線程釋放其執行資格 和 執行權,並存儲到線程池中。


notify():喚醒,喚醒線程池中被wait()的線程,一次喚醒一個,而且是任意的。


notifyAll(): 喚醒全部:可以將線程池中的所有wait() 線程都喚醒。


  

public class NumberHolder
{    private int number;    public synchronized void increase()
    {        if (0 != number)
        {            try
            {
                wait();
            }            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }        // 能執行到這裏說明已經被喚醒        // 並且number爲0
        number++;
        System.out.println(number);        // 通知在等待的線程        notify();
    }    public synchronized void decrease()
    {        if (0 == number)
        {            try
            {
                wait();
            }            catch (InterruptedException e)
            {
                e.printStackTrace();
            }

        }        // 能執行到這裏說明已經被喚醒        // 並且number不爲0
        number--;
        System.out.println(number);
        notify();
    }

}public class IncreaseThread extends Thread
{    private NumberHolder numberHolder;    public IncreaseThread(NumberHolder numberHolder)
    {        this.numberHolder = numberHolder;
    }

    @Override    public void run()
    {        for (int i = 0; i < 20; ++i)
        {            // 進行一定的延時
            try
            {
                Thread.sleep((long) Math.random() * 1000);
            }            catch (InterruptedException e)
            {
                e.printStackTrace();
            }            // 進行增加操作            numberHolder.increase();
        }
    }

}public class DecreaseThread extends Thread
{    private NumberHolder numberHolder;    public DecreaseThread(NumberHolder numberHolder)
    {        this.numberHolder = numberHolder;
    }

    @Override    public void run()
    {        for (int i = 0; i < 20; ++i)
        {            // 進行一定的延時
            try
            {
                Thread.sleep((long) Math.random() * 1000);
            }            catch (InterruptedException e)
            {
                e.printStackTrace();
            }            // 進行減少操作            numberHolder.decrease();
        }
    }

}public class NumberTest
{    public static void main(String[] args)
    {
        NumberHolder numberHolder = new NumberHolder();
        
        Thread t1 = new IncreaseThread(numberHolder);
        Thread t2 = new DecreaseThread(numberHolder);
                
        t1.start();
        t2.start();
    }

}

因爲就是兩個線程所以就是可以互相切換,即必須是互相切換,但是如果是四個線程呢?會怎麼樣呢?

那麼再來兩個線程;

 即把其中的NumberTest類改爲如下:

public class NumberTest
{    public static void main(String[] args)
    {
        NumberHolder numberHolder = new NumberHolder();
        
        Thread t1 = new IncreaseThread(numberHolder);
        Thread t2 = new DecreaseThread(numberHolder);
        
        Thread t3 = new IncreaseThread(numberHolder);
        Thread t4 = new DecreaseThread(numberHolder);
                
        t1.start();
        t2.start();
        
        t3.start();
        t4.start();
    }

}

爲什麼兩個線程的時候執行結果正確而四個線程的時候就不對了呢?

  因爲線程在wait()的時候,接收到其他線程的通知,即往下執行,不再進行判斷。兩個線程的情況下,喚醒的肯定是另一個線程;但是在多個線程的情況下,執行結果就會混亂無序

比如,一個可能的情況是,一個增加線程執行的時候,其他三個線程都在wait,這時候第一個線程調用了notify()方法,其他線程都將被喚醒,然後執行各自的增加或減少方法。

  解決的方法就是:在被喚醒之後仍然進行條件判斷,去檢查要改的數字是否滿足條件,如果不滿足條件就繼續睡眠。把兩個方法中的if改爲while即可。

public class NumberHolder
{    private int number;    public synchronized void increase()
    {        while (0 != number)
        {            try
            {
                wait();
            }            catch (InterruptedException e)
            {
                e.printStackTrace();
            }
        }        // 能執行到這裏說明已經被喚醒        // 並且number爲0
        number++;
        System.out.println(number);        // 通知在等待的線程        notify();
    }    public synchronized void decrease()
    {        while (0 == number)
        {            try
            {
                wait();
            }            catch (InterruptedException e)
            {
                e.printStackTrace();
            }

        }        // 能執行到這裏說明已經被喚醒        // 並且number不爲0
        number--;
        System.out.println(number);
        notify();
    }

}

這樣就可以解決了線程的混亂的問題。

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