在Java中无限期线程等待

时间:2016-08-05 16:11:19

标签: java multithreading synchronization wait notify

我正在尝试使用等号通知使用2个线程生成奇数/偶数。

但它只是打印 1

以下是代码:

Even.java

public class Even implements Runnable {
    private int i; private Object ob
    public Even(int i,Object o) {
    this.i=i;
    this.ob=o;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (ob) {
                while (i % 2 == 0) {
                    try {
                        ob.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                i++;
                System.out.println(i);
                ob.notifyAll();
            }
        }
    }
}

Odd.java

public class Odd implements Runnable {
    private int i; private Object ob;
    public Odd(int i) {
        this.i=i;
        this.ob=o;
    }
    @Override
    public void run() {
        while (true) {
            synchronized (ob) {
                while (i % 2 == 1) {
                    try {
                        ob.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                i++;
                System.out.println(i);
                ob.notifyAll();
            }
        }
    }
}

Test.java

public class Test {
    public static void main(String[] args) {
        int i = 0;
        Object lock = new Object();
        Thread t1 = new Thread(new Even(i),lock);
        Thread t2 = new Thread(new Odd(i),lock);
        t1.start();
        t2.start(); 
    }       
}

输出:

1

有谁能告诉我我犯了哪个错误?

3 个答案:

答案 0 :(得分:1)

嗯,我认为这可能是一个问题,Integer似乎在Java中是不可变的。因此,如果两个类中的一个更改了i值(由于第一个输出为1,它显然会这样做),您将不再同步相同的对象。

因此,您更改iOdd中存储的值并在新对象上调用notifyAll,因为您在一个对象上调用notifyAll,所以Java会抱怨实际上从未被锁定过。

答案 1 :(得分:1)

线程已进入 INDEFINITE WAITING 状态。请注意(t1,即Event2的实例,即Odd的实例),每个都有一个实例变量i的单独副本。

以下是幕后的内容:

  • 最初,帖子t1Even)和t2Odd)都在 READY 中,然后 RUNNABLE
  • 让我们假设t1被安排并让处理器执行。 t1进入 RUNNING 状态。调用t1的{​​{1}}
    • run()成功并且控制进入外部循环
    • 因为while(true) synchronized(ob)锁定了对象t1
    • 因为ob最初是i; 0条件评估为i % 2 == 0
    • control现在进入内部while循环体,然后true阻止并调用try
    • ob.wait();进入等待状态并等待有人通知对象t1
  • 现在,ob被安排并让处理器执行。 t2进入 RUNNING 状态
    • t2成功并且控制进入外部循环
    • 因为while(true) synchronized(ob)锁定了对象t2
    • ob最初为i(请记住先前增加的0i - t1Even的本地值。此上下文中的变量ii的本地变量,即t2Odd
    • 因此i评估为i % 2 == 1,控件跳过内部循环
    • 控制范围false i++;i增加到0
    • 语句1System.out.println(i);打印到控制台
    • contorl移动到下一行,调用1并通知所有等待对象ob.notifyAll();(在我们的例子中为ob)的线程
  • 此时,t1t1再次回到 RUNNABLE 状态
  • 取决于处理哪个线程来安排
  • 让我们假设t2被安排并让处理器执行
    • t1从之前离开的位置恢复其操作(即t1之后的声明)
    • 控制到达ob.wait();并且由于没有异常,因此跳过并控制回到catch (InterruptedException e)检查
    • 请注意,while ( i % 2 == 0)的{​​{1}}(即t1i)仍为Even,因为控件无法到达{{1}行} i
    • 因此0评估为i++;并且控件进入while循环体,然后进入try块并调用Even
    • i % 2 == 0再次进入等待状态并等待有人通知对象ob
  • 现在,true被安排并让处理器执行。 ob.wait();进入 RUNNING 状态
    • t1从之前离开的位置恢复其操作(即t2之后的声明)
    • 因为在t2之后没有其他语句,所以控件到达外部while循环
    • t2被评估并且控制进入外部循环体
    • 因为ob.notifyAll(); ob.notifyAll();锁定了对象while(true)
    • 请记住synchronized(ob)的{​​{1}}现在是t1,因为它先前已增加并打印在控制台上
    • 因此,ob被评估为t2并且控件进入内部循环体,然后i阻止并调用1
    • 因此,while ( i % 2 == 1)进入等待状态
  • truetry现在都处于 WAITING 状态;等待对象ob.wait();。等待某人通过对象t2通知他们。可悲的是,没有人可以拯救

因此 INDEFINITE WAITING

以下代码应该有助于您实现的目标

t1

注意:

  1. 我重构了你的代码,以便线程尝试获取锁定并等待的对象也保存数据。
  2. t2对象现在是obob个线程之间的共享实例。
  3. 根据我之前的推理,代码应该是自我解释的。
  4. 这不是并行计算中的实际操作方式,而是利用多线程功能/功能。但是,它应该是一个很好的入门练习。

答案 2 :(得分:0)

您可以将i变量作为类属性放入Test类中,然后创建一个Test类型的对象,并将其作为参数传递给构造函数:

public class Test {

public Integer i = 0;

public static void main(String[] args) {
    Object o = new Object();
    Test t = new Test();
    Thread t1 = new Thread(new Even(t,o));
    Thread t2 = new Thread(new Odd(t,o));
    t1.start();
    t2.start(); 
}       

}

public class Even implements Runnable {
private Test t;
private Object o;
public Even(Test t, Object o) {
this.t=t;
this.o=o;
}
@Override
public void run() {
    while (true) {
        synchronized (o) {
            while (t.i % 2 == 0) {
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            t.i++;
            System.out.println(t.i);
            o.notifyAll();
        }
    }
}

}

public class Odd implements Runnable {
private Test t;
private Object o;
public Odd(Test t, Object o) {
    this.t=t;
    this.o = o;
}
@Override
public void run() {
    while (true) {
        synchronized (o) {
            while (t.i % 2 == 1) {
                try {
                    o.wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            t.i++;
            System.out.println(t.i);
            o.notifyAll();
        }
    }
}

}