【问题标题】:Queue to gather outputs from threads从线程收集输出的队列
【发布时间】:2013-10-02 04:49:04
【问题描述】:

我有一个应用程序并行执行两个线程,每个线程执行一些操作,最终生成一个整数。在我的主线程中,我想对每个线程 A 和线程 B 产生的 int 执行一些操作(分别称为 int a 和 int b),然后按照每个线程吐出它们的顺序输出它们。所以本质上,我定期运行每个线程(比如每 2 秒),每个线程都会吐出一个整数,我按照它们的创建顺序操作和打印出来。

为了做到这一点,我如何将需要由 gui 线程打印的每个线程的 int 聚合在一起?我可以为主线程观察到的每个线程使用一个队列,当它更新时,主 gui 操作并输出它们?这样的队列怎么写?

我的线程代码如下:

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        new Thread(new t1()).start(//TODO: pass in some parameter that this
                                   //thread will write to); 
        new Thread(new t2()).start();
    }
}, 0, period);

【问题讨论】:

  • 队列,等待()和通知()。这些是您的关键字。现在没时间详细说明,抱歉。

标签: java multithreading


【解决方案1】:

考虑一下这个解决方案,您可能会发现它很有用。它使用ConcurrentLinkedQueue 对队列进行线程安全操作。

final Queue<Integer> queue1 = new ConcurrentLinkedQueue<Integer>();
final Queue<Integer> queue2 = new ConcurrentLinkedQueue<Integer>();

final Random r = new Random();

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {             
    @Override
    public void run() {
        //Thread 1
        new Thread(new Runnable() {
            @Override
            public void run() {
                //even numbers
                queue1.add(r.nextInt(50)*2);
            }
        }).start();

        //Thread 2
        new Thread(new Runnable() {
            @Override
            public void run() {
                //odd numbers
                queue2.add(r.nextInt(50)*2 + 1);
            }
        }).start();
    }
}, 0, 2000);

//Main thread (maybe GUI)
while (true){
    while (!queue1.isEmpty()){
        System.out.println("Thread-1: " + queue1.poll());
    }
    while (!queue2.isEmpty()){
        System.out.println("Thread-2: " + queue2.poll());
    }
}

编辑

您真正需要的是生产者-消费者(请参阅http://en.wikipedia.org/wiki/Producer%E2%80%93consumer_problem)。为了解决这个问题,我们将使用另一个队列ArrayBlockingQueue,因为这个数据结构让我们阻塞,直到有东西产生(或消费)。

final BlockingQueue<Integer> queue1 = new ArrayBlockingQueue<Integer>(1024);
final BlockingQueue<Integer> queue2 = new ArrayBlockingQueue<Integer>(1024);

final Random r = new Random();          

Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
    @Override
    public void run() {
        try {
            //don't create threads here, this is already a thread, just produce the numbers
            //If the queue is full `BlockingQueue.put()` will block until the consumer consume numbers.

            //Producer even number
            queue1.put(r.nextInt(50)*2);

            //Producer odd number
            queue2.put(r.nextInt(50)*2 + 1);

        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}, 0, 2000);

//Now we create the threads that will take numbers from the producer. Don't worry about the `while (true)`, it is not wasting resources because `BlockingQueue.take()` will block the thread until something is produced.

//Consumer 1
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            while (true){
                System.out.println("Thread-1: " + queue1.take());

                //Or update some UI component
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

//Consumer 2
new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            while (true){
                System.out.println("Thread-2: " + queue2.take());

                //Or update some UI component
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}).start();

【讨论】:

  • 问题是,计时器无法从 run() 中直接访问队列。队列位于另一个类中。所以我可以把它传入,当我在 run() 中添加时,另一个类中的队列也会看到它被添加。换句话说,队列会被 ref 传递吗?
  • @JohnBaum 访问队列是另一回事,您可以按照自己喜欢的方式进行操作。出于演示目的,我使用了一个最终变量,但你可以有一个单例,从一个对象调用一个 getter,等等......
  • 是一段时间(真)继续检查新队列数据是否可用的唯一方法吗?有没有我可以使用的不阻塞用户界面的基于通知的系统?
  • @JohnBaum 我使用while(true),因为我的线程不是一个GUI 线程,比如AWT“事件调度线程”,它一直在等待(本机)事件。在 GUI 应用程序中,您可能希望在事件发生时检查队列(例如单击按钮)。
  • 感谢您的澄清!只是,我的线程在计时器的后台运行,因此没有真正的“事件”触发它们,更像是每 x 秒运行线程并使用结果更新 ui。那我怎么知道什么时候更新屏幕呢?
【解决方案2】:

尝试使用Executor 框架,它使用Callables 而不是Runnables。使用 Callable 的一个优点是它提供了一个类似于 Runnable 的run 方法的call 方法,但call 方法可以return 一个值。因此,在您的情况下,您可以从两个线程返回整数值,然后可以在主线程中使用它们。

【讨论】:

    【解决方案3】:

    将队列传递给两个线程的构造函数。

    重写你传递的队列的offer/add方法,并在此处添加你调用gui主线程的逻辑,基本上调用基类调用后调用监听器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-11-05
      • 2013-06-18
      • 2013-01-14
      • 2019-09-17
      • 2013-01-03
      • 1970-01-01
      • 1970-01-01
      • 2010-09-19
      相关资源
      最近更新 更多