zoukankan      html  css  js  c++  java
  • Java 多线程(七) 线程间的通信

    Java 多线程(七) 线程间的通信——wait及notify方法

     

    线程间的相互作用

      线程间的相互作用:线程之间需要一些协调通信,来共同完成一件任务。

      Object类中相关的方法有两个notify方法和三个wait方法:

      http://docs.oracle.com/javase/7/docs/api/java/lang/Object.html

      因为wait和notify方法定义在Object类中,因此会被所有的类所继承。

      这些方法都是final的,即它们都是不能被重写的,不能通过子类覆写去改变它们的行为。

     

    wait()方法

      wait()方法使得当前线程必须要等待,等到另外一个线程调用notify()或者notifyAll()方法。

      当前的线程必须拥有当前对象的monitor,也即lock,就是锁。

      线程调用wait()方法,释放它对锁的拥有权,然后等待另外的线程来通知它(通知的方式是notify()或者notifyAll()方法),这样它才能重新获得锁的拥有权和恢复执行。

      要确保调用wait()方法的时候拥有锁,即,wait()方法的调用必须放在synchronized方法或synchronized块中。

      一个小比较:

      当线程调用了wait()方法时,它会释放掉对象的锁。

      另一个会导致线程暂停的方法:Thread.sleep(),它会导致线程睡眠指定的毫秒数,但线程在睡眠的过程中是不会释放掉对象的锁的。

     

    notify()方法

      notify()方法会唤醒一个等待当前对象的锁的线程。

      如果多个线程在等待,它们中的一个将会选择被唤醒。这种选择是随意的,和具体实现有关。(线程等待一个对象的锁是由于调用了wait方法中的一个)。

      被唤醒的线程是不能被执行的,需要等到当前线程放弃这个对象的锁。

      被唤醒的线程将和其他线程以通常的方式进行竞争,来获得对象的锁。也就是说,被唤醒的线程并没有什么优先权,也没有什么劣势,对象的下一个线程还是需要通过一般性的竞争。

      notify()方法应该是被拥有对象的锁的线程所调用。

      (This method should only be called by a thread that is the owner of this object's monitor.)

      换句话说,和wait()方法一样,notify方法调用必须放在synchronized方法或synchronized块中。

      wait()和notify()方法要求在调用时线程已经获得了对象的锁,因此对这两个方法的调用需要放在synchronized方法或synchronized块中。

      一个线程变为一个对象的锁的拥有者是通过下列三种方法:

      1.执行这个对象的synchronized实例方法。

      2.执行这个对象的synchronized语句块。这个语句块锁的是这个对象。

      3.对于Class类的对象,执行那个类的synchronized、static方法。

     

    程序实例

      利用两个线程,对一个整形成员变量进行变化,一个对其增加,一个对其减少,利用线程间的通信,实现该整形变量0101这样交替的变更。

    NumberTest
    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类改为如下:

    NumberTest 4线程
    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();
        }
    
    }

      运行后发现,加上t3和t4之后结果就错了。

      为什么两个线程的时候执行结果正确而四个线程的时候就不对了呢?

      因为线程在wait()的时候,接收到其他线程的通知,即往下执行,不再进行判断。两个线程的情况下,唤醒的肯定是另一个线程;但是在多个线程的情况下,执行结果就会混乱无序。

      比如,一个可能的情况是,一个增加线程执行的时候,其他三个线程都在wait,这时候第一个线程调用了notify()方法,其他线程都将被唤醒,然后执行各自的增加或减少方法。

      解决的方法就是:在被唤醒之后仍然进行条件判断,去检查要改的数字是否满足条件,如果不满足条件就继续睡眠。把两个方法中的if改为while即可。

    NumberHolder 4线程
    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();
        }
    
    }

    参考资料

      圣思园张龙老师Java SE系列视频教程。

  • 相关阅读:
    PyQt作品 – PingTester – 多点Ping测试工具
    关于和技术人员交流的一二三
    Pyjamas Python Javascript Compiler, Desktop Widget Set and RIA Web Framework
    Hybrid Qt applications with PySide and Django
    pyjamas build AJAX apps in Python (like Google did for Java)
    PyQt 维基百科,自由的百科全书
    InfoQ:请问为什么仍要选择Java来处理后端的工作?
    Eric+PyQt打造完美的Python集成开发环境
    python select module select method introduce
    GUI Programming with Python: QT Edition
  • 原文地址:https://www.cnblogs.com/mengdd/p/2917956.html
Copyright © 2011-2022 走看看