Java-使用wait/notify进行线程通信

 

在多线程编程中,通常要求一个线程等待另一个线程采取某些措施。

语义上,根据基础线程模型不可能进行直接线程通信,相反,开发人员可以使用基于共享变量的某些条件来实现线程间通信。

让我们考虑以下示例,其中thread1打印一条消息,而thread2填充相同的消息字符串:

public class SimpleWaitNotifyDemo1 {
	private static String message;

	public static void main(String[] args) {

		Thread thread1 = new Thread(() -> {
			System.out.println(message);
		});

		Thread thread2 = new Thread(() -> {
			message = "A message from thread1";
		});

		thread1.start();
		thread2.start();
	}
}

 

由于线程调度程序的不可预测性,我们不能保证线程1将打印由线程2填充的消息。线程1必须有某种机制来向线程2发信号,通知已填充共享消息。以下是开发人员可以想到的最常见的解决方案:

public class SimpleWaitNotifyDemo2 {
	private static String message;

	public static void main(String[] args) {

		Thread thread1 = new Thread(() -> {
			while (message == null) {
			}
			System.out.println(message);
		});

		Thread thread2 = new Thread(() -> {
			message = "A message from thread1";
		});

		thread1.start();
		thread2.start();
	}
}
上面的thread1的while循环被称为保护块“ Guarded Blocks”,但这并不是一种有效的解决方案,因为此循环浪费CPU周期,我们也无法避免线程干扰 。

Java扩展了它的“固有锁定”模型提供更好的解决方案,即使用java.lang.Object的wait()和notify()/notifyAll()方法。

所有这些Object的方法调用都重定向到本机方法,这意味着该机制由底层操作系统固有地提供。

让我们修改上面的示例以查看其工作方式:

public class SimpleWaitNotifyDemo {
	private static String message;

	public static void main(String[] args) {
		Object lock = new Object();

		Thread thread1 = new Thread(() -> {
			synchronized (lock) {
				while (message == null) {
					try {
						lock.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}

			System.out.println(message);
		});

		Thread thread2 = new Thread(() -> {
			synchronized (lock) {
				message = "A message from thread1";
				lock.notify();
			}
		});

		thread1.start();
		thread2.start();
	}
}

 

Object.wait()和Object.notify()

wait()导致当前线程等待(阻止或挂起执行),直到另一个线程为此对象调用notify()方法或notifyAll()方法。

wait()方法抛出InterruptedException-如果相应的线程调用interrupt(),则等待状态将被中断(就像sleep()方法一样)。引发此异常时,将清除当前线程的中断状态。

与不释放“内在锁”的sleep方法不同,此方法释放“内在锁”。这意味着在该线程处于等待状态时,其他线程有机会执行其他同步的块/方法,这对于总体性能是有利的。

IllegalMonitorStateException如果当前线程不是对象监视器的所有者(内部锁),则也会引发此方法调用。这就是我们在上面的示例中使用同步块的原因。

在上面的示例中,我们也可以改用同步方法。让我们修改上面的示例,以将同步和线程通信整合到单个对象:

public class WaitNotifyDemo {

    public static void main (String[] args) {
        SharedObject obj = new SharedObject();

        Thread thread1 = new Thread(() -> {
            obj.printMessage();
        });

        Thread thread2 = new Thread(() -> {
            obj.setMessage("A message from thread1");
        });

        thread1.start();
        thread2.start();
    }

    private static class SharedObject {
        private String message;

        public synchronized void setMessage (String message) {
            this.message = message;
            notify();
        }

        public synchronized void printMessage () {
            while (message == null) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println(message);

        }
    }
}

 

notify()vs notifyAll()

notify()唤醒在该对象的监视器上等待的单个线程(固有锁定)。如果有多个线程在等待,那么只有一个线程会收到通知或将被唤醒;我们无法预测哪个线程将被通知,这完全取决于调度程序。

notifyAll()将导致所有线程在由于wait()调用而处于等待状态时唤醒。

由于notify()和notifyAll()也从同步的方法/块中调用:等待线程将不会开始执行,直到调用这些方法的线程释放了锁。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值