【问题标题】:How to make a thread wait for a specific condition如何让线程等待特定条件
【发布时间】:2019-10-31 00:14:36
【问题描述】:
有一个线程计算素数并将它们添加到集合中。
现在有其他线程将执行bool isPrime(long n) 方法。如果集合中包含数字 (n),则此方法只会查看集合。
但是执行isPrime(...)的线程需要等到:
- 号码已添加
- 有一个大于 n 的数,所以我知道 n 不能是素数。
并且它只允许在这种情况下通知线程。所以我无法在添加到集合中的每个数字后通知并检查它是否为数字或更高。
您能否在不忙于等待的情况下给我一些关于这种等待条件的解释?
我只知道我可以让线程等待对象,而其他线程可以通知它们,但我无法理解这种行为,也许我只是想错了。
【问题讨论】:
标签:
java
multithreading
thread-safety
【解决方案1】:
这真的取决于你的整体设计。
一个简单的解决方案可以这样工作:
- 你有 1 个线程的 prime-generator 和 n 个线程的 prime-testers
- 最初所有主要测试人员都致电
wait()
- 每次 prime-generator 添加一个新的 prime,它都会通知 所有 prime-testers
- 每个测试人员检查其号码(或更大号码)是否已经在,如果测试人员找到它的号码,或者知道“不在”。如果没有,它会再次调用
wait()。
该解决方案的巨大优势:素数生成器不需要知道存在多少素数测试器。它只是通知所有在一个公共监视器上等待的线程。
或者,质数生成器可以准确地知道存在哪些质数测试者,以及它们负责的数量。因此,它不会唤醒所有测试人员,而是只通知需要知道的测试人员。
请理解:您只是给出了一些模糊的要求,没有任何代码。因此,您会收到一个模糊的答案,没有任何代码。我的回答旨在作为指导你下一步作业的灵感。
仅作记录:如果您想获得非常大的素数,那么使用列表是一个糟糕的选择。假设您的列表包含 100 万个素数。调用contains() 的费用将随条目数线性增长。所以宁愿使用一个集合,它允许快速查找元素(某种集合/树),但也可以快速访问集合中当前“最后一个”(最大)的数字。
【解决方案2】:
@GhostCat 回答的基本思路变成了代码:
import java.util.HashSet;
import java.util.Set;
public class PrimeThreading {
// All prime numbers found so far.
private static final Set<Long> primes = new HashSet<>();
// Last number checked by the generator.
private static long numbersChecked = 0L;
// The lock object.
private static final Object lock = new Object();
private static class PrimeGenerator implements Runnable {
private final long maxNumber = Long.MAX_VALUE;
@Override
public void run() {
// Generate all prime numbers from 2 to maxNumber
for (long n = 2; n < maxNumber; n++) {
// Naively test if n is prime.
boolean isPrime = true;
for (long i = 2; i * i < n; i++) {
if (n % i == 0) {
isPrime = false;
break;
}
}
synchronized (lock) {
if (isPrime) {
primes.add(n);
}
numbersChecked = n;
// Notify waiting threads
lock.notifyAll();
}
}
}
}
private static boolean isPrime(long x) {
synchronized (lock) {
// Wait until number checked is greater than x
while (x > numbersChecked) {
try {
lock.wait();
} catch (InterruptedException e) {
break;
}
}
return primes.contains(x);
}
}
public static void main(String[] args) {
Thread thread = new Thread(new PrimeGenerator());
thread.setDaemon(true);
thread.start();
System.out.println(isPrime(15_485_863));
}
}