线程通信


讲解等待唤醒机制之前,有必要搞清一个概念——

线程之间的通信:

多个线程在处理同一个资源,但是处理的动作(线程的任务)却不相同。通过一定的手段使各个线程能有效的利用资源。而这种手段即—— 等待唤醒机制。


等待唤醒机制所涉及到的方法:


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

}

这样就可以解决了线程的混乱的问题。

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