【问题标题】:Java - multithreading and synchronizationJava - 多线程和同步
【发布时间】:2012-03-22 18:49:01
【问题描述】:

我有两个非常相似的程序,每个程序都尝试运行两个线程 OddThread 和 EvenThread 并尝试按顺序打印奇数和偶数。第一个工作时,第二个挂起。谁能指出第二个程序中的错误?

第一个有效的:

public class ThreadTest {

public static void main(String[] args) {
    System.out.println("Odd Even test");
    NumHolder objNumHolder = new NumHolder();
    Odd o1 = new Odd(objNumHolder, "Odd Number Thread");
    Even e1 = new Even(objNumHolder, "Even Number Thread");
    o1.start();
    e1.start();
}
}

class NumHolder {
private int intCurrNum;
private boolean isEven = false;

public synchronized void printOddNumber(String tname) {
    while (isEven == true){
        try {
            wait();
        }catch (InterruptedException e) {
        }
    }
    isEven = true;
    System.out.println("Thread Name="+tname + "===Number="+intCurrNum);
    intCurrNum += 1;
    notifyAll();
}

public synchronized void printEvenNumber(String tname) {
    while (isEven == false) {
        try {
            wait();
        } catch (InterruptedException e) {
        }
    }
    isEven = false;
    System.out.println("Thread Name="+tname + "===Number="+intCurrNum);
    intCurrNum += 1;
    notifyAll();
}
}

class Even extends Thread {
private NumHolder objNumHolder;

public Even(NumHolder p_objNumHolder, String name) {
    super(name);
    objNumHolder=p_objNumHolder;
}

public void run() {
    for (int i = 0; i < 10; i++) {
        objNumHolder.printEvenNumber(getName());
    }
}
}
class Odd extends Thread {
private NumHolder objNumHolder;

public Odd(NumHolder p_objNumHolder,String name) {
    super(name);
    objNumHolder = p_objNumHolder;
}

public void run() {
    for (int i = 0; i < 10; i++) {
        objNumHolder.printOddNumber(getName());
    }
}
}

挂起的第二个代码:

class PrintClass {
int intCurrNum;
private boolean isEven = false;

synchronized void printOdd(){
    while(isEven){
        try{
            wait();
        }catch(InterruptedException ie){
            System.out.println("Interrupted exception in printOdd()");
            ie.printStackTrace();
        }
        isEven = true;
        System.out.println("Thread Name="+Thread.currentThread().getName() + "===Number="+intCurrNum);
        intCurrNum += 1;
        notifyAll();
    }
}

synchronized void printEven(){
    while(!isEven){
        try{
            wait();
        }catch(InterruptedException ie){
            System.out.println("Interrupted exception in printEven()");
            ie.printStackTrace();
        }
        isEven = false;
        System.out.println("Thread Name="+Thread.currentThread().getName() + "===Number="+intCurrNum);
        intCurrNum += 1;
        notifyAll();
    }
}
}
class ThreadOdd extends Thread {
PrintClass pc = null;

ThreadOdd(PrintClass pc , String name){
    super(name);
    this.pc = pc;
}

public void run(){
    for (int i = 0; i < 10; i++) {
        pc.printOdd();
    }
}
}
class ThreadEven extends Thread {
PrintClass pc = null;

ThreadEven(PrintClass pc,String name){
    super(name);
    this.pc = pc;
}

public void run(){
    for (int i = 0; i < 10; i++) {
        pc.printEven();
    }
}
}
public class EvenOddPrintClass {
public static void main(String[] args){
    PrintClass pc = new PrintClass();
    Thread to = new ThreadOdd(pc,"ThreadOdd");
    Thread te = new ThreadEven(pc,"ThreadEven");
    to.start();
    te.start();
}
}

谢谢。

【问题讨论】:

  • 这个作业有关系吗?如果一个线程“挂起”,可能是因为您的第一个线程锁定了第二个线程试图获取锁定的资源;或者,它可能正在等待信号,但线程一从未发出信号。我们需要知道wait函数里面是什么,否则很难分辨。
  • @Lirik:这是来自 java.lang.Object 的等待方法。另一个线程继续等待另一个线程在 printOdd/printEven 方法结束时使用 notifyAll 调用将其唤醒,但它们可能会卡在方法同步或其他线程从 while 中失败-loop 和其他被卡住 wait():in for the notify.
  • @esaj... 哦,明白了。我忘了!

标签: java multithreading synchronization wait notify


【解决方案1】:

我建议您在调试器中运行您的代码并单步执行两个线程。这很有教育意义。您将看到错误的确切位置。

【讨论】:

    【解决方案2】:

    在两个版本中,isEven 都以 false 开头。

    在第一个版本中,printOddNumber 将跳过整个while 循环,打印奇数,设置isEven 为true 并通知even 线程,它将打印偶数并再次通知odd 线程等. 按顺序。

    在第二个版本中,printOddNumber 将跳过整个while 循环,包括打印数字和通知even 线程。尝试 10 次后,它将退出而不打印任何内容,并让 even 线程挂起而从未通知它。

    【讨论】:

      【解决方案3】:

      有趣。所以最初是isEven = false。如果首先调用printOdd(),则while (isEven) 测试为假,因此printOdd()立即退出而不产生任何输出。第一个程序中的while 循环仅包含wait 测试,而不是整个方法。

      那么当printEven()被其他线程调用时,它会调用wait()并挂起,因为没有其他线程可以调用notifyAll()

      您应该只希望while 围绕wait 循环,因为无论如何在打印出偶数或奇数后您都将退出,对吧?所以第一个程序中的逻辑是正确的。

      【讨论】:

        【解决方案4】:
        public class CountDownApp
         {
         public static void main(String[] args)
         {
         Thread count1 = new CountDownEven();
         Thread count2 = new CountDownOdd();
         count1.start();
         count2.start();
        
         }
         }
        
         class CountDownEven extends Thread
         {
         public void run()
         {
         for(int i=10;i>0;i-=2)
         { 
         System.out.print(+i+"-");
        
         try {
            Thread.sleep(2);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
         }
         }
        
         }
        
         class CountDownOdd extends Thread 
         {
         public void run()
         {
         for(int i=9;i>0;i-=2)
         {
         System.out.print(+i+"-");
        
         try {
                Thread.sleep(2);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
         }
         }
        
         }
        

        【讨论】:

        • 抱歉,我不想为此使用 sleep()。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-07-14
        • 1970-01-01
        相关资源
        最近更新 更多