什么是轮询,它有什么问题?
反复测试条件直到其变为真的过程称为轮询。
轮询通常是借助循环来实现的,以检查特定条件是否为真。如果为真,则将采取某些措施。这浪费了许多CPU周期,并使实现效率低下。
例如,在一个经典的排队问题中,一个线程正在产生数据而另一个线程正在消耗数据。
Java多线程如何解决这个问题?
为了避免轮询,Java使用三种方法,即wait(),notify()和notifyAll()。
所有这些方法最终都属于对象类,因此所有类都具有它们。它们只能在同步块中使用。
- wait()-它告诉调用线程放弃锁定并进入睡眠状态,直到其他线程进入同一监视器并调用notify()为止。
- notify()-唤醒一个在同一对象上调用wait()的单个线程。应该注意的是,调用notify()实际上并没有放弃对资源的锁定。
- notifyAll()-唤醒同一对象上所有调用wait()的线程。
一个简单的Java程序来演示这三种方法
// 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
如果您仍然对为什么我们在消耗线程中使用了通知感到困惑,请尝试将其删除并再次运行您的程序。正如您现在必须已经注意到的那样,该程序永远不会终止。
这样做的原因很简单-当您在生产线程上调用wait时,它继续等待并且从未终止。由于程序一直运行到其所有线程终止,因此它将一直运行。
解决这个问题还有第二种方法。您可以使用wait()的第二种变体。
void wait(long timeout)
这将使调用线程仅在指定的时间内休眠。