我知道之前已经问过这个问题,但我无法弄清楚为什么我的解决方案对我不起作用。我有两个线程偶数和奇数,一个打印偶数和其他打印奇数。当我启动线程时,我希望输出是自然的数字顺序,如0 1 2 3 ..等。这是我的代码: - [更新]
public class ThreadCommunication {
public static void main(String... args) throws InterruptedException
{
final ThreadCommunication obj = new ThreadCommunication();
Thread even = new Thread(){
@Override
public void run()
{
for(int i=0;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
try {
obj.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
obj.notify();
}
}
}
};
even.start();
odd.start();
}
}
当我运行上面的代码时,有时会按照预期自然顺序打印数字,但有时它会以其他顺序打印出来:
0
1
3
5
7
9
2
我在这里做错了什么?
答案 0 :(得分:2)
修改强>
volatile static boolean isAlreadyWaiting = false;
Thread even = new Thread() {
@Override
public void run() {
synchronized (obj) {
for (int i = 0; i < 10; i = i + 2) {
System.out.println(i);
try {
if (!isAlreadyWaiting) {
isAlreadyWaiting = true;
obj.wait();
}
obj.notify();
isAlreadyWaiting=false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread() {
@Override
public void run() {
synchronized (obj) {
for (int i = 1; i < 10; i = i + 2) {
System.out.println(i);
try {
if(isAlreadyWaiting){
obj.notify();
isAlreadyWaiting = false;
}
if (!isAlreadyWaiting) {
isAlreadyWaiting = true;
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
检查文档
公共类IllegalMonitorStateException扩展RuntimeException
抛出表示某个帖子试图等待一个对象&#39> 监视或通知在对象监视器上等待的其他线程 没有拥有指定的监视器。
Monitor由obj
所以你应该打电话给
obj.wait();
和
obj.notify();
有关所有权的更多信息
此方法(等待或通知)只应由一个线程调用 是该对象监视器的所有者。线程成为的所有者 对象的监视器有三种方式之一:
- 执行该对象的同步实例方法。
- 醇>
通过执行同步的同步语句的主体 在对象上。
- 对于Class类型的对象,通过执行同步静态方法 那个班。
一次只能有一个帖子拥有对象的监视器。
答案 1 :(得分:0)
@Pragnani Kinnera对你看到的例外是对的。但是,如果要在even
和odd
之间切换,则需要将第二个同步块移动到循环中。否则,通知线程将保持锁定,直到循环完成。 (与第一个线程相反,它会在每一轮产生锁定。)
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
synchronized(obj){
System.out.println(i);
notify();
}
}
}
};
然而,第一个线程应该在synchronized块中有循环。如果两个线程都释放锁定,则它们都有相同的机会重新获取它。但是如果第一个循环在synchronized块内,第二个线程将无法重新进入,直到第一个循环完成一整轮并再次等待。
编辑:这仍然无法正常工作,因为根据documentation的引用,无法保证第一个线程不会在第二个线程执行之前重新获取锁定:
唤醒线程将以通常的方式与可能主动竞争同步此对象的任何其他线程竞争;例如,唤醒线程在下一个锁定此对象的线程中没有可靠的特权或劣势。
您可能希望从两个线程中唤醒并通知它们以确保它们处于同步状态。
答案 2 :(得分:-1)
以下是您的解决方案:
public class ThreadCommunication {
public static void main(String args[]) throws InterruptedException
{
final ThreadCommunication obj = new ThreadCommunication();
Thread even = new Thread("Even Thread"){
@Override
public void run()
{
for(int i=0;i<10;i=i+2){
System.out.println(i);
synchronized(obj){
obj.notify();
}
synchronized(obj){
try {
obj.wait();
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
};
Thread odd = new Thread(){
@Override
public void run()
{
for(int i=1;i<10;i=i+2){
try {
synchronized(obj){
obj.wait();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(i);
synchronized(obj){
obj.notifyAll();
}
}
}
};
even.start();
odd.start();
}
}
正如@shmosel所解释的,您的同步块应该只包含需要同步的代码。