线程行为

时间:2012-03-30 05:40:08

标签: java thread-safety

从Thread的角度来看,什么是块,等待和锁定?相反,是否有必要在任何操作中拥有所有这三个?例如,在生产者 - 消费者模式中如何实现这一点。 提前致谢

3 个答案:

答案 0 :(得分:2)

阻塞操作是阻止线程直到操作完成的操作。 阻塞线程是告诉线程调度程序(通常是操作系统,尽管有用户级线程库)在该线程被唤醒之前不运行线程的过程。阻塞操作有很多种,其中一个例子是文件I / O.与任何其他阻塞操作一样,该方法在相关操作(在本例中为文件I / O)完成之前不会返回。

wait 是一种用于线程同步的特殊阻塞操作。具体来说,它表示"请阻止调用wait的线程,直到某个其他线程唤醒它。"在Java中,等待是method。相应的唤醒方法是notify

lock 是一种更高级别的抽象,它表示"只允许有限数量的线程进入这个代码区域。"最常见的是,有限数量是1,在这种情况下,互斥体(我在this SO answer中详细解释)是像C这样的低级语言中的首选锁定原语。在Java中,最常见的锁定原语称为监视器。有一种概念是拥有一个对象的监视器(每个对象都有一个监视器),并在监视器上等待,并唤醒正在监视器上等待的线程。我们如何做到这一点?您猜对了 - 我们使用wait方法在监视器上等待,并notify唤醒监视器上等待的其中一个线程。

现在回答可能听起来有点像希腊语,因为你刚刚开始使用并发:要实现生产者 - 消费者模式,最常见的策略是使用两个信号量(加上一个互斥量来同步访问缓冲)。信号量通常用互斥体实现,但是它是一个高阶构造,因为它允许计算一些资源。因此,您要保留一个信号量来计算缓冲区中的项目数,并使用一个信号量来计算缓冲区中的空白数。生成器等待空的空间信号量,并在空间可用时将项添加到缓冲区,并且消费者等待项信号量并在项可用时消耗项。

现在我已经定义了这些东西是什么,但我还没有真正谈到如何使用它们。然而,这在大学课程中值得几个讲座,对于StackOverflow答案肯定是太多了。我建议the Java tutorials中的并发课程作为开始使用线程的方法。另外,在网上查找大学课程。许多学校在网上公开发布笔记,因此只需稍加搜索,您就可以找到高质量的材料。


编辑:等待和阻止I / O之间差异的描述

在开始阅读本文之前,请确保您熟悉一个主题是什么,以及一个过程是什么。我在this SO answer的前四段中给出了解释,Wikipedia有更详细的解释(尽管历史背景较少)。

每个线程都有一条非常重要的信息:一个指令指针(还有与每个线程相关的其他重要信息,但它们现在并不重要)。指令指针是JVM维护的指针,指向当前正在执行的字节码指令。每次执行一条指令时(每条指令都是一个非常简单的操作的抽象表示,例如对象foo上的"调用方法x),指令指针会向前移动到某些& #34;下一条指令。"为了运行程序,JVM将指令指针设置为main的开头并继续执行指令并向前移动指令指针,直到程序以某种方式退出。

阻塞操作阻止指令指针向前移动,直到某些事件发生,导致指令指针再次向前移动。当然,启动阻塞操作的线程不能使这个事件发生,因为该线程的指令指针不会向前移动,即该线程什么都不做。

现在,有很多种不同的阻止操作。一个是阻止I / O.例如,如果您致电System.out.println,则println方法不会返回,直到文本被写入控制台。在这种情况下,指令指针停在System.out.println内的某处,操作系统指示线程在控制台打印完成时唤醒。因此线程不必再次启动自己的指令指针,但该方法仍然在文本写入控制台后返回。所以,在很高的层面上:

  • 线程0调用System.out.println("foo")
  • 线程0的指令指针停止移动,而操作系统写入" foo"到控制台
  • 当操作系统写入控制台时,它会通知JVM,JVM会自动开始移动线程0的指令指针再次移动。所有这一切都发生在没有编写System.out.println的程序员必须考虑它的情况下。

另一种完全独立的类阻塞操作封装在Object.wait方法中。每当线程调用Object.wait时,该线程的指令指针停止移动,而不是操作系统再次开始移动指令指针,另一个线程完成该工作。在这种情况下,没有外部事件会导致线程的指令指针重新启动(如阻塞I / O情况),但程序内部的事件。正如我所说,另一个线程将通过调用Object.notify再次启动指令指针。所以,在很高的层面上:

  • 线程0在某个对象上调用x.wait()
  • 线程0的指令指针停止移动
  • 主题1在同一个对象x.notify()
  • 上调用x
  • 线程0的指令指针再次开始移动
  • 线程0和线程1现在正在并发执行

请注意,正确编写等待/通知代码需要做更多的工作 - 这次JVM和操作系统不会为您完成所有工作。他们实际上仍然为您完成大部分工作,但实际上您必须考虑调用waitnotify,以及它们如何允许您在线程之间进行通信,实现锁等等。

所以这个故事有两个道德。第一个是阻塞I / O和wait是完全不同的野兽。在这两种情况下,线程都被阻塞,但在阻塞I / O情况下,操作系统会自动唤醒线程,而在wait情况下,线程必须依赖另一个调用{{1}的线程。为了唤醒它。第二,并发编程比串行编程更难以推理。我在这个答案中提出的玩具示例并没有真正做到第二点正义。

答案 1 :(得分:0)

不,您不一定需要锁定或等待,因为您正在使用线程。但是,如果您希望线程交换数据,它们通常很有用。

以下是消费者生产者模型示例的一个很好的解释:

http://www.ase.md/~aursu/JavaThreadsSynchronization.html

干杯!

答案 2 :(得分:0)

阻止:阻止执行。
等待:暂停当前主题。
锁定:当您锁定时,其他人无法使用它。
当客户购买电影票时,请考虑在线购买 一旦他选择了座位。其他人将无法同时获得这些座位(锁定这些座位)。