0

我正在测试一个 Java 多线程示例代码,但是在 qB.start() 的 for 循环中启动的线程被阻止,因为它正在等待 qB 监视器的进入。这种堵塞的原因是什么?

谢谢你。

import java.util.*;

class QA {

public synchronized void open() throws Exception {

    Thread o = new Thread() {

        public void run() {

            QB qB = new QB();

            qB.start();
        }
    };

    o.start();
}

public static void main(String args[]) throws Exception {

    new QA().open();
}

public class QB {

private boolean shutdown;
private Vector<Thread> tList;
private final Object waitingLock = new Object();

public QB() {

    tList = new Vector<Thread>();
}

public synchronized void start() {


    for(int i = 0; i < 1; i++) {

        final int id = i;

        Thread t = new Thread("Thread " + id) {

            public void run() {

                load(id);
            }
        };

        tList.add(i, t);

        t.start();

    }

    tMonitor();
    waitUntilFinished();
}

private void tMonitor() {

    Thread cmt = new Thread("T Monitor Thread") {

        public void run() {

            synchronized(waitingLock) {

                while(tList.size() > 0) {

                    try {

                        sleep(10000);

                    } catch(Exception e) {

                        e.printStackTrace();
                    }
                }

                waitingLock.notifyAll();
            }
        }
    };

    cmt.start();
}

private void waitUntilFinished() {

    synchronized(waitingLock) {

        while(!isShutDown()) {

            try {


                waitingLock.wait();


            } catch(Exception e) {

        e.printStackTrace();
            }
        }
    }

}

private synchronized void load(int id) {

    try {

        System.out.println("blocked here");

// some work done here

removeFromTList(id);


    } catch(Exception e) {

        e.printStackTrace();
    }
}


public synchronized boolean isShutDown() {

    return shutdown;
}
}
}
4

2 回答 2

3

我看到的第一个问题QB#start()是在QB. t在您尝试生成的线程内部,load(id)也在QB. 因此,当您调用线程块时,直到t.start()完成。tQB#start()

据推测,在QB#start()方法结束时,QB#waitUntilFinished()应该等待所有t线程完成,但它们甚至无法进入QB#load方法,因为它们仍在等待QB#start()方法释放QB实例上的锁。

所以,循环僵局。

于 2012-04-11T22:32:08.943 回答
1

编辑:

好的,现在我们tList已经完全揭示了如何从错误中删除线程。

如果索引 0 线程首先完成,则它将自己从列表中删除。这意味着当索引 1 线程完成时,它将从第 1 个位置移除Vector但不再指向自身。它正在删除#2 线程。当删除发生时,您迟早会遇到异常,因为它将删除无效索引。

您需要从Vector按地址而不是按位置删除项目:

 tList.remove(this);

这将从列表中删除当前线程。您还应该在开始循环中执行 anadd(t)而不是 an :add(i t)

 tList.add(t);

您现在根本不需要将 id 位置传递到您的线程中。


我看不到您从tList. 我看到了一个方法的定义(不是你编辑了你的 OP),removeFromTList()但我没有看到它在任何地方使用。在tMonitor你在这里的一个while循环中:

        while(tList.size() > 0) {
            try {
                sleep(10000);
            } catch(Exception e) {
                e.printStackTrace();
            }
        }
        // you never get to this line 
        waitingLock.notifyAll();

但我没有看到任何从列表中删除线程的内容。也许当每个线程完成时,它们应该自行移除?

如果tMonitor线程永远不会退出该循环,那么它永远不会调用:

waitingLock.notifyAll();

所以主线程将永远挂在waitUntilFinished();.

synchronized(waitingLock) {
    while(!isShutDown()) {
        try {
            waitingLock.wait();
        } catch(Exception e) {
            e.printStackTrace();
        }
    }

此外,你不想做一个sleepintMonitor()因为你在一个synchronized街区。你应该做一个:

  waitingLock.wait(10000);

什么都不会通知它,但在睡眠中保持这样的锁是不好的形式。

于 2012-04-11T22:15:09.210 回答