初学者Java多线程问题

时间:2015-11-22 21:56:11

标签: java multithreading

我遇到了一个Java多线程文件爬虫问题。我的问题是我有一个workQueue,它是一个linkedBlockingQueue,它包含我想用我的线程抓取的文件的名称,每个线程将->来自workQueue,并且在扫描文件时它可能take()另一个文件名放入workQueue(它是一个依赖检查器程序)。因此,当我们完成所有工作并且所有线程最终都会从(最终)空的workQueue中尝试put()时,所有线程最终都会进入等待状态时,我永远不会确定。

所以我想我的问题是,一旦完成所有工作(当所有线程都进入等待状态时)是否有一种终止所有线程的有效方法?目前我只在主线程上使用take(),然后在所有工作线程中使用sleep()

对不起,如果这个问题听起来很混乱。

3 个答案:

答案 0 :(得分:1)

之前我遇到过这个问题,我发现的唯一方法就是向BlockingQueue发送一个特殊的标记对象。当队列.take()对象时,如果这是标记,则Thread会自行结束。

我尝试过其他解决方案,比如唤醒线程并检测异常,但没有成功。

答案 1 :(得分:1)

有一种称为Poison Pill的模式对此有好处。基本上,当生成器完成后,在队列中插入一个特殊值,告诉消费者停止。您可以为每个消费者插入一个药丸,或者,一旦消费者获得毒丸,将其返回到下一个消费者的队列中。因为听起来你只是在将字符串排列,比如

public static final String POISON_PILL = "DONE";

或者在Java 8中,使用Optional来包装你的值,然后就不会出现问题。

BlockingQueue<Optional<...>> queue;

另一种选择是使用ExecutorService(实际上由BlockingQueue支持)并将每个文件作为自己的任务提交,然后在完成后使用executorService.shutdown()。这样做的问题在于它比你需要的代码更紧密地耦合代码,这使得重用资源(如数据库和HTTP连接)变得更加困难。

我会避免打断你的工作人员发出信号,因为这会导致阻止IO操作失败。

答案 2 :(得分:0)

您可以使用下面的approch。如果需要,添加观察者模式。 或者简单地 - 不是使用死亡数据包发信号,而是收集等待线程的列表,然后中断()它们。

public class AccessCountingLinkedPrioQueue<T> {

    private final LinkedBlockingQueue<T>    mWrappingQueue                  = new LinkedBlockingQueue<>();
    private final Object                    mSyncLockObj                    = new Object();

    private final int                       mMaxBlockingThreads;
    private final T                         mDeathSignallingObject;

    private volatile int                    mNumberOfThreadsInAccessLoop    = 0;

    public AccessCountingLinkedPrioQueue(final int pMaxBlockingThreads, final T pDeathSignallingObject) {
        mMaxBlockingThreads = pMaxBlockingThreads;
        mDeathSignallingObject = pDeathSignallingObject;
    }

    public T take() throws InterruptedException {
        final T retVal;
        synchronized (mSyncLockObj) {
            ++mNumberOfThreadsInAccessLoop;
        }
        synchronized (mWrappingQueue) {
            if (mNumberOfThreadsInAccessLoop >= mMaxBlockingThreads && mWrappingQueue.isEmpty()) signalDeath();
            retVal = mWrappingQueue.take();
        }
        synchronized (mSyncLockObj) {
            --mNumberOfThreadsInAccessLoop;
        }
        return retVal;
    }

    private void signalDeath() {
        for (int i = 0; i < mMaxBlockingThreads; i++) {
            mWrappingQueue.add(mDeathSignallingObject);
        }
    }

    public int getNumberOfThreadsInAccessLoop() {
        return mNumberOfThreadsInAccessLoop;
    }
}

class WorkPacket {
    // ... your content here
}

class MultiThreadingBoss {
    static public final WorkPacket  DEATH_FROM_ABOVE    = new WorkPacket();

    public MultiThreadingBoss() {
        final int THREADS = 7;
        final AccessCountingLinkedPrioQueue<WorkPacket> prioQ = new AccessCountingLinkedPrioQueue<>(THREADS, DEATH_FROM_ABOVE);
        for (int i = 0; i < THREADS; i++) {
            final ThreadedWorker w = new ThreadedWorker(prioQ);
            new Thread(w).start();
        }
    }
}

class ThreadedWorker implements Runnable {
    private final AccessCountingLinkedPrioQueue<WorkPacket> mPrioQ;

    public ThreadedWorker(final AccessCountingLinkedPrioQueue<WorkPacket> pPrioQ) {
        mPrioQ = pPrioQ;
    }

    @Override public void run() {
        while (true) {
            try {
                final WorkPacket p = mPrioQ.take();
                if (p == MultiThreadingBoss.DEATH_FROM_ABOVE) break; // or return

                // ... do your normal work here

            } catch (final InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}