【发布时间】:2014-07-01 00:48:09
【问题描述】:
目前我正在阅读在线Java Concurrency 教程的Guarded Blocks 章节。作为一个练习,我创建了一个类来在实践中查看 wait() 和 notifyAll() 方法的正确使用。然而,我自己的代码中有一些东西我无法理解,如果您能帮我一把,我将不胜感激。
环境:
OS: Fedora Core 17 X86_64
JDK: 1.8.0_05 (64 Bit)
测试用例规范:
- 定义一个创建和启动 4 个线程的类,
- 每个线程的run()方法实际上是一个无限循环,当用户按CTRL+C时会停止,
- 每个线程必须在 {A, B, C, D} 中打印一个字母,
- 无论四个创建的线程中的哪一个是当前运行的线程,都必须尊重字母的字母顺序 与上一个打印的字母比较。
- 首先打印字母“A”
因此,预期的输出在终端上是这样的:
A
B
C
D
A
B
C
D
A
B
C
D
...
测试用例实现:
/*
My solution is based on a shared lock among threads.
This object has one attribute: a letter, indicating
the letter that must be printed on the user terminal.
*/
class SharedLock
{
private char letter;
public SharedLock(char letter)
{
this.letter = letter;
}
/*
Every thread which is owner of the shared lock's
monitor call this method to retrieve the letter
that must be printed according to the alphabetic order.
*/
public synchronized char getLetter()
{
return this.letter;
}
/*
Every thread which is the owner of the shared lock's
monitor and besides has just printed its letter, before
releasing the ownership of the shared lock's monitor,
calls this method in order to set the next
letter (according to the alphabetic order) to
be printed by the next owner of the shared
lock's monitor
*/
public synchronized void setLetter(char letter)
{
this.letter = letter;
}
}
/*
As said earlier each thread has a letter attribute.
So if I create 4 threads, there will be one thread
for each letter, one which prints only 'A', another
which prints only 'B', and so on.
Besides each thread's constructor takes as second
parameter: the shared lock object (described above).
If the letter attribute of a thread which is the owner
of the shared lock's monitor, is the same as
the shared lock's letter attribute, then the thread can
print its letter because it respects the alphabetic order
otherwise it has to wait.
*/
class LetterPrinter implements Runnable
{
private char letter;
private SharedLock lock;
public LetterPrinter(char letter, SharedLock lock)
{
this.letter = letter;
this.lock = lock;
}
public void run()
{
while(true)
{
// Here the current thread tries to become the owner of
// the shared lock's monitor
synchronized(this.lock)
{
/*
Test whether the letter attribute of this
thread must be printed. This will happen
only if the letter of the shared lock and
the thread's letter attribute are the same.
*/
while(this.lock.getLetter() != this.letter)
{
try
{
// The letters are different so in order to respect
// the alphabetic order this thread has to wait
this.lock.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
}
// printing the letter
System.out.format("%s: %s%n",
Thread.currentThread().getName(), this.letter);
// preparing for the next letter print according to the
// alphabetic order
switch (this.letter)
{
case 'A': this.lock.setLetter('B'); break;
case 'B': this.lock.setLetter('C'); break;
case 'C': this.lock.setLetter('D'); break;
case 'D': this.lock.setLetter('A'); break;
}
// And finally releasing the ownership of
// the shared lock's monitor
synchronized(this.lock)
{
this.lock.notifyAll();
}
}
}
}
public class MyTestClass
{
public static void main(String[] args)
{
// creating the shared lock object which is initialized
// by the letter 'A'. This was the problem specification
// we wish to start by 'A'
SharedLock lock = new SharedLock('A');
// Creates the four threads with their distinct letter and
// their shared lock
Thread thread1 = new Thread(new LetterPrinter('A', lock));
Thread thread2 = new Thread(new LetterPrinter('B', lock));
Thread thread3 = new Thread(new LetterPrinter('C', lock));
Thread thread4 = new Thread(new LetterPrinter('D', lock));
// And starting all of the four created threads above.
thread1.start();
thread2.start();
thread3.start();
thread4.start();
}
}
这个程序实际上产生了所需的输出,并且在我看来可以正确地完成工作(如果我错了,请纠正我)。但是,如果您查看上面的 run() 方法,您会发现最后 notify() 调用也已放置在同步块中。
为了看看会发生什么,我消除了同步块,我只写了 notify() 来释放锁的监视器的所有权,我得到了
Exception in thread "Thread-0" java.lang.IllegalMonitorStateException
at java.lang.Object.notifyAll(Native Method)
at LetterPrinter.run(MyTestClass.java:105)
at java.lang.Thread.run(Thread.java:745)
根据IllegalMonitorStateException的文档:
公共类 IllegalMonitorStateException 扩展 RuntimeException
抛出表明一个线程试图等待一个对象的 监视或通知等待对象监视器的其他线程 不拥有指定的监视器。
这正是我的问题。为什么?
为什么在当前所有者释放共享锁的所有权时,通知调用也必须放在同步块中?
根据notify()和notifyAll()的文档:
线程在以下三种情况之一中成为对象监视器的所有者 方式:
- 通过执行该对象的同步实例方法。
- 通过在对象上执行同步语句的主体。
- 对于 Class 类型的对象,通过执行该类的同步静态方法。
一次只有一个线程可以拥有一个对象的监视器。
第二个,即锁上的同步语句是我所做的。因此,每个不是好的线程(根据字母顺序)都会等待。因此,当锁上的 notify() 被执行时,它只能由作为其监视器所有者的线程运行,并且没有其他线程可以尝试运行它,因为所有其他线程都在等待。
所以我不明白为什么将 notify() 调用放在同步块之外的 run() 方法的末尾会引发 IllegalMonitorStateException 异常?
我是并发方面的初学者。显然,我误解了语句的执行和操作系统调度程序。
有人可以澄清一下吗?
【问题讨论】:
-
“拥有监视器”意味着在适当的
synchronizedblock 内。观点。其他线程做什么都没关系。 -
@Holger 非常感谢您的评论。
标签: java multithreading concurrency synchronization