Java线程按顺序排列

时间:2014-01-18 04:45:13

标签: java multithreading

我正在学习线程所以我想制作一个有两种线程类型的程序:一种是写随机数,另一种是检查当前数字是否与某个特定数字匹配。线程从Numbers类调用write()和read(int)方法。为了使事情更清楚,我希望我的主程序看起来像这样:

Numbers n = new Numbers();
new WritingThread(n);
new ReadingThread(n,3);
new ReadingThread(n,5);

所以输出会是这样的:

2 
7
3 !!! MATCH !!!
8
5 !!! MATCH !!!
1
...

问题是线程没有按顺序执行。我想首先执行WritingThread,然后执行所有的ReadingThreads。因为这样会写入一个新的随机数,只有一个线程有机会检查数字是否匹配。这是代码:

班级数字

public class Numbers {
int number;
boolean written = false;

public synchronized void write() {
    while (written)
        try {
            wait();
        } catch (InterruptedException e1) {
            e1.printStackTrace();
        }

    number = (int) (Math.random() * 10);
    System.out.print("\n" + number);
    written = true;
    notifyAll();
}

public synchronized void check(int n) {
    while (!written)
        try {
            wait();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    System.out.print(" Reading thread: "    + Thread.currentThread().getName());
    if (n == number) 
        System.out.print(" !!! MATCH !!! ");
    notify();
    written = false;
}
}

类WritingThread

public class WritingThread extends Thread {
    Numbers n;
    WritingThread(Numbers n){
        this.n = n;
        start();
    }

    public void run(){
        while(true){
            n.write();
        }
    }
}

类ReadingThread

public class ReadingThread extends Thread{
Numbers n;
int number;
public ReadingThread(Numbers n, int number){
    this.n = n;
    this.number = number;
    start();
}

public void run(){
    while(true){
        n.check(number);
    }
}
}

输出:

3 Reading thread: Thread-2
3 Reading thread: Thread-1 !!! MATCH !!! 
0 Reading thread: Thread-2
5 Reading thread: Thread-1
0 Reading thread: Thread-2
0 Reading thread: Thread-1
5 Reading thread: Thread-2 !!! MATCH !!! 
8 Reading thread: Thread-1

我知道我可以制作一个有一系列数字的线程来检查,但我很好奇怎么可能这样做。感谢。

1 个答案:

答案 0 :(得分:2)

让我们从你的例子开始。您有两个使用者和一个布尔标志。通过逻辑思考。让我们调用我们的三个帖子WC1C2

  • W post 5
  • W设置标记为true
  • W发送notifyAll
  • C2清醒
  • C1清醒
  • C2获取锁定
  • C1阻止
  • C2不匹配
  • C2通知
  • W清醒
  • W blocks
  • C2释放锁定
  • C1获取锁定
  • flag为false,C1等待(释放监视器)
  • flag为false,C2等待(释放监视器)
  • GOTO开始

如果此代码可以通过多种方式获得乐趣,那么这只是一个。每当需要获取锁定时,所有人都可以免费获得等待它的线程只有一个可以获得锁定。该线程将检查值集并重置标志。如果该线程不是该值所针对的线程仍然被消耗。

你有一个种族危险应该是相当明显的。您正在为两个使用者线程使用单个队列。每个消费者线程都在争夺队列。您的队列是线程安全的,因为任何时候只有一个线程可以从中读取单个项目,但它会导致竞争危险,因为每个消费者线程都希望是唯一一个读取它的人。如果错误的线程读取该项,则另一个线程无法看到它。

解决此问题的唯一方法是每个线程有一个队列。生产者将相同的项放入每个消费者线程的私有队列中,每个消费者线程从其队列中获取项目并读取它们。

以下是使用ExecutorSerivce

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

    final class Consumer implements Runnable {

        private final BlockingQueue<Integer> q = new LinkedBlockingDeque<>();
        private final int search;

        public Consumer(final int search) {
            this.search = search;
        }

        @Override
        public void run() {
            while (true) {
                try {
                    if (q.take() == search) {
                        System.out.println("Found magic number.");
                    }
                } catch (InterruptedException ex) {
                    return;
                }
            }
        }

        public Queue<Integer> getQ() {
            return q;
        }
    }

    final class Producer implements Runnable {

        final Random r = new Random();
        final Iterable<Queue<Integer>> qs;

        public Producer(final Iterable<Queue<Integer>> qs) {
            this.qs = qs;
        }

        @Override
        public void run() {
            while (true) {
                final int i = r.nextInt();
                for (final Queue<Integer> q : qs) {
                    q.offer(i);
                }
            }
        }
    }

    final int numConsumers = 5;
    final Collection<Queue<Integer>> qs = new LinkedList<>();
    final ExecutorService es = Executors.newCachedThreadPool();
    for (int i = 0; i < numConsumers; ++i) {
        final Consumer c = new Consumer(i);
        qs.add(c.getQ());
        es.submit(c);
    }
    es.submit(new Producer(qs));
}

使用Random.nextInt()时,您可能很少点击此示例。如果您希望获得更多匹配,请拨打生成数字Random.nextInt(int max)的{​​{1}}来缩小生成的随机数的范围。

正如您所看到的,每个[0, max)都有一个要检查的项目队列,并使用Consumer API阻止等待新项目。 BlockingQueue依次将相同的项目放入每个Producer的队列中。