# 问题和练习的答案:并发
# 问题
你可以传递一个 Thread 对象 Executor.execute 吗?这样的调用是否有意义?
Thread
实现了 Runnable 接口,这样你就可以通过 Executor.execute 执行Thread
实例。 但是,以Thread
这种方式使用对象是没有意义的。如果对象直接实例化Thread
,其 run 方法不执行任何操作。 您可以继承Thread
并重写run
方法定义一个子类,但是这样一个类将实现执行器不会使用的功能。
# 练习
以下代码怎么修改才能让主线程中的 message 始终都赋值成功,且打印出来?是否可以调整两个
sleep
的参数?如何保证始终都有效?解决方案: 程序几乎总是打印:"Mares do eat oats." 但是这个结果并不能保证,因为 两个修改语句之间没有发生关系。有两种方法可以保证:
- 在主线程中,保留对 CorrectorThread 实例的引用。然后 join
CorrectorThread correctorThread = new CorrectorThread(); correctorThread.start(); correctorThread.join(); message = "Mares do not eat oats."; System.out.println(message);
1
2
3
4
5- 封装 message 在具有同步方法的对象中。message 除了通过这些方法外,不要参考。
这两种技术都建立了
happens-before
的关系,使得 message 可见的变化。第三种技术是简单地声明 message 为 volatile。它们可能会顺序发生,但是由于调度的不确定性和未知的粒度 sleep,这是不能保证的。
改变两个 sleep 调用的参数也没有帮助,因为这并不能保证在关系之前发生。
修改 守护块 中的生产者 - 消费者示例,以使用标准库类而不是
Drop
类
解决方案:
java.util.concurrent.BlockingQueue
接口定义了 get 阻塞队列为空,以及阻止队列已满的 put 方法。
这些是有效的定义的操作 Drop
除了 Drop
不是队列! 所以在前面的例子中,直接把 Drop 换成 BlockingQueue
即可
BlockingQueue
几乎是 Drop 一个替代品。在主要的问题是 Producer
,随着 BlockingQueue 中,put 和 get 方法抛出 InterruptedException
。
这意味着现有的 try
必须向上移动一个级别: