Java中的线程间通信

作者: Arvin Chen 分类: Java 来源: Break易站(www.breakyizhan.com)

什么是Polling 以及它有什么问题?

重复测试条件直到它变为真的过程称为轮询Polling 
轮询通常在循环的帮助下实现,以检查特定条件是否为真。如果是,则采取某些行动。这浪费了许多CPU周期并使实现效率低下。
例如,在一个经典的排队问题中,一个线程正在生成数据而另一个正在使用它。

Java多线程如何解决这个问题?

为了避免轮询,Java使用三种方法,即wait(),notify()和notifyAll()。
所有这些方法都属于对象类,因此所有类都有它们。它们必须仅在同步块中使用。

  • wait() -它告诉调用线程放弃锁定并进入休眠状态,直到某个其他线程进入同一个监视器并调用notify()。
  • notify() -它唤醒一个在同一个对象上调用wait()的线程。应该注意的是,调用notify()实际上并没有放弃对资源的锁定。
  • notifyAll() -它唤醒在同一个对象上调用wait()的所有线程。

一个简单的Java程序来演示这三种方法 -

请注意,这个程序可能只在离线IDE中运行,因为它包含在几个点上输入。

// Java program to demonstrate inter-thread communication
// (wait(), join() and notify()) in Java
import java.util.Scanner;
public class threadexample
{
    public static void main(String[] args)
                           throws InterruptedException
    {
        final PC pc = new PC();

        // Create a thread object that calls pc.produce()
        Thread t1 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    pc.produce();
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });

        // Create another thread object that calls
        // pc.consume()
        Thread t2 = new Thread(new Runnable()
        {
            @Override
            public void run()
            {
                try
                {
                    pc.consume();
                }
                catch(InterruptedException e)
                {
                    e.printStackTrace();
                }
            }
        });

        // Start both threads
        t1.start();
        t2.start();

        // t1 finishes before t2
        t1.join();
        t2.join();
    }

    // PC (Produce Consumer) class with produce() and
    // consume() methods.
    public static class PC
    {
        // Prints a string and waits for consume()
        public void produce()throws InterruptedException
        {
            // synchronized block ensures only one thread
            // running at a time.
            synchronized(this)
            {
                System.out.println("producer thread running");

                // releases the lock on shared resource
                wait();

                // and waits till some other method invokes notify().
                System.out.println("Resumed");
            }
        }

        // Sleeps for some time and waits for a key press. After key
        // is pressed, it notifies produce().
        public void consume()throws InterruptedException
        {
            // this makes the produce thread to run first.
            Thread.sleep(1000);
            Scanner s = new Scanner(System.in);

            // synchronized block ensures only one thread
            // running at a time.
            synchronized(this)
            {
                System.out.println("Waiting for return key.");
                s.nextLine();
                System.out.println("Return key pressed");

                // notifies the produce thread that it
                // can wake up.
                notify();

                // Sleep
                Thread.sleep(2000);
            }
        }
    }
}

输出:

producer thread running
Waiting for return key.
Return key pressed
Resumed

虽然看起来很怪异,但如果你经历两次它真的是小菜一碟。

  1. 在主类中创建一个新的PC对象。
  2. 它使用两个不同的线程即t1和t2运行生成和使用PC对象的方法,并等待这些线程完成。

让我们了解我们的产品和消费方法是如何工作的。

  • 首先,使用synchronized块可确保一次只运行一个线程。此外,由于在消耗循环开始时存在一个sleep方法,因此生成线程获得kickstart。
  • 在produce方法中调用wait时,它会做两件事。首先,它释放它在PC对象上的锁定。其次,它使生成线程进入等待状态,直到所有其他线程终止,也就是说它可以再次获取PC对象的锁定,并且其他一些方法通过在同一对象上调用notify或notifyAll来唤醒它。
  • 因此我们看到,一旦调用wait,控件就转移到使用线程并打印 - “等待返回键”。
  • 按下返回键后,consume方法调用notify()。它还做了两件事 - 首先,与wait()不同,它不会释放对共享资源的锁定,因此为了获得所需的结果,建议仅在方法结束时使用notify。其次,它通知等待的线程现在它们可以唤醒,但只有在当前方法终止后才能唤醒。
  • 正如您可能已经观察到的那样,即使在通知之后,控件也不会立即转移到生产线程。原因是我们在notify()之后调用了Thread.sleep()。正如我们已经知道消费线程在PC对象上持有一个锁,另一个线程在释放锁之前无法访问它。因此,只有在消费线程完成其休眠时间并且此后自行终止之后,产生线程才能收回控制。
  • 暂停2秒后,程序终止完成。

如果您仍然对我们在使用消费线程中使用notify的原因感到困惑,请尝试删除它并再次运行程序。正如您现在必须注意到的,该程序永远不会终止。
原因很简单 - 当您在生产线程上调用wait时,它继续等待并且从未终止。由于程序运行直到其所有线程都已终止,因此它会一直运行。
这个问题还有第二种方法。您可以使用wait()的第二个变体。

void wait(long timeout)

这将使调用线程仅在指定的时间内休眠。

  •   本文标题:Java中的线程间通信 - Break易站
    转载请保留页面地址:https://www.breakyizhan.com/java/4941.html
    扫描二维码添加微信 
  • ,领取淘宝优惠券,淘宝购物更优惠。现在添加微信,还可以领取机械键盘优惠券!添加微信后,分享淘宝选中的机械键盘给淘宝机器人即可领取!
    支持我们,就用微信淘宝!

    发表笔记

    电子邮件地址不会被公开。 必填项已用*标注

    更多阅读