線程通訊


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

線程之間的通訊:ide

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


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


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


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


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

}

由於就是兩個線程因此就是能夠互相切換,即必須是互相切換,可是若是是四個線程呢?會怎麼樣呢?it

那麼再來兩個線程;io

 即把其中的NumberTest類改成以下:class

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

}

這樣就能夠解決了線程的混亂的問題。

相關文章
相關標籤/搜索