如何解决这个线程阻塞问题

时间:2012-04-11 22:10:46

标签: java multithreading blocking

我正在测试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;
}
}
}

2 个答案:

答案 0 :(得分:3)

我看到的第一个问题是在QB#start()的实例上QB已同步。 在您尝试生成的线程t内,load(id)也在同一QB实例上同步。因此,在t.start()完成之前调用t QB#start()个线程块。

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

所以,循环僵局。

答案 1 :(得分:1)

修改

好的,现在我们看到如何从tList中删除线程,这个bug已经完全显示出来了。

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

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

 tList.remove(this);

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

 tList.add(t);

你现在根本不需要传递给你线程的id位置。


我不知道您从tList删除已完成的帖子的位置。我看到了removeFromTList()方法的定义(不是您编辑了OP),但我没有看到它在任何地方使用过。在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();
        }
    }

另外,您不想在sleeptMonitor()进行synchronized,因为您处于 waitingLock.wait(10000); 区块。你应该做的是:

{{1}}

没有什么能够通知它,但在睡眠中保持锁定是不好的形式。