我正在学习线程所以我想制作一个有两种线程类型的程序:一种是写随机数,另一种是检查当前数字是否与某个特定数字匹配。线程从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
我知道我可以制作一个有一系列数字的线程来检查,但我很好奇怎么可能这样做。感谢。
答案 0 :(得分:2)
让我们从你的例子开始。您有两个使用者和一个布尔标志。通过逻辑思考。让我们调用我们的三个帖子W
,C1
和C2
。
W
post 5 W
设置标记为true
W
发送notifyAll
C2
清醒C1
清醒C2
获取锁定C1
阻止C2
不匹配C2
通知W
清醒W blocks
C2
释放锁定C1
获取锁定C1
等待(释放监视器)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
的队列中。