队列工作线程停止工作,线程安全问题?

时间:2014-07-10 06:06:14

标签: java multithreading thread-safety

我想首先介绍我的问题。

我有几个正在接收字符串的WorkingThreads,处理字符串,然后将处理后的字符串发送到全局队列,如下所示:

class Main {
  public static Queue<String> Q;
  public static void main(String[] args) {
    //start working threads
  }
}

WorkingThread.java:

class WorkingThread extends Thread {
  public void run() {
    String input;
    //do something with input
    Main.q.append(processedString);
}

所以现在每隔800ms另一个名为Inserter的Thread将所有条目出列,以制定一些sql,但这并不重要。

class Inserter extends Thread {
  public void run() {
    while(!Main.Q.isEmpty()) {
      System.out.print(".");
      // dequeue and formulate some SQL
    }
  }
}

一切都可以工作大约5到10分钟,但突然间,我看不到任何打印点(基本上是插入器的心跳)。队列不是空的我可以保证,但即使它开始正常,插入器也不会工作。

我怀疑当一个工作者想要在插入器使队列出列时插入某些东西时会出现问题,这可能是某种“死锁”吗?

我真的希望有人能解释这种行为。我期待着学习;)。

编辑:我正在使用

Queue<String> Q = new LinkedList<String>();

3 个答案:

答案 0 :(得分:2)

您没有使用synchronized或线程安全Queue,因此您有种族危险。您对LinkedList的使用表明(略微可怕)缺乏对此事实的了解。在尝试解决任何更多线程代码之前,您可能想要阅读有关线程和线程安全性的更多信息。

必须手动同步或使用JDK提供的现有实现之一。生产者/消费者模式通常使用BlockingQueue实现之一来实现。

如果队列已满,则有限大小的BlockingQueue将阻止生成器尝试put。如果队列为空,BlockingQueue将始终阻止使用者。

这允许您删除在队列中旋转并等待项目的所有自定义逻辑。

使用Java 8 lambdas的一个简单示例如下所示:

public static void main(String[] args) throws Exception {
    final BlockingQueue<String> q = new LinkedBlockingQueue<>();
    final ExecutorService executorService = Executors.newFixedThreadPool(4);
    final Runnable consumer = () -> {
        while (true) {
            try {
                System.out.println(q.take());
            } catch (InterruptedException e) {
                return;
            }
        }
    };
    executorService.submit(consumer);
    final Stream<Runnable> producers = IntStream.range(0, 5).mapToObj(i -> () -> {
        final Random random = ThreadLocalRandom.current();
        while (true) {
            q.add("Consumer " + i + " putting " + random.nextDouble());
            try {
                TimeUnit.MILLISECONDS.sleep(random.nextInt(2000));
            } catch (InterruptedException e) {
                //ignore
            }
        }
    });
    producers.forEach(executorService::submit);
}

consumer方法上的BlockingQueue.take块,并且有一个项目可用,它将被唤醒并打印该项目。如果没有项目,则线程将被挂起 - 允许物理CPU执行其他操作。

producers每个String使用addadd推送到队列中。由于队列无限制,consumer将始终返回true。在可能存在工作积压的情况下,对于put,您可以绑定队列并使用InterruptedException方法(抛出try..catch,因此需要add这就是为什么它更容易使用{{1}}) - 这将自动创建流控制。

答案 1 :(得分:1)

您将需要同步对您的队列的访问权限 使用ConcurrentLinkedQueue请参阅http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/ConcurrentLinkedQueue.html

或建议使用BlockingQueue(取决于您的要求)http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/BlockingQueue.html

有关BlockingQueue的更详细说明,请参阅

http://tutorials.jenkov.com/java-util-concurrent/blockingqueue.html

答案 2 :(得分:1)

似乎更像同步问题..您正在尝试模拟 - Producer - Consumer problem。您需要同步队列或使用BlockingQueue。你可能有竞争条件。