当其中一个线程找到质数时如何停止线程

How to stop threads when one of them finds a prime number

当发现素数时,我必须阻止它。当我在while之前使用synchronized时,只会发生一个线程进程。但是,应该会发生多线程操作,但是当找到素数时,所有操作都应该停止。

控制部分i的初始值已更改

我想做的是使用lock和synchronized找质数。

public abstract class NumberGenerator {

    private boolean isStop;

    public abstract int generateNumber();

    public void stop() {
        this.isStop = true;
    }

    public boolean isStopped() {
        return isStop;
    }

}

public class IntegerNumberGenerator extends NumberGenerator {

    private Random random;
    int randomAtama;

    public IntegerNumberGenerator() {
        this.random = new Random();
    }

    @Override
    public int generateNumber() {
        return random.nextInt(100) + 1;
    }
}

public class PrimeNumberChecker implements Runnable {

    private NumberGenerator generator;
    private Lock lock = new ReentrantLock();
    public Condition continueLock = lock.newCondition();

    public PrimeNumberChecker(NumberGenerator generator) {
        this.generator = generator;
    }

    @Override
    public void run() {

        while (!generator.isStopped()) {

            int number = generator.generateNumber();
            System.out.println(Thread.currentThread().getName() + " generated " + number);
            if (check(number)) {
                System.out.println(number + " is prime !");
                generator.stop();
            }
        }
    }

    public static boolean check(int number) {

        boolean result = true;
        for (int i = 2; i <= number / 2; i++) {
            if ((number % i) == 0) {
                result = false;
            }
        }
        return result;
    }
}

public class Driver {

    public static void main(String[] args) {

        ExecutorService executorService = Executors.newCachedThreadPool();
        NumberGenerator numberGenerator = new IntegerNumberGenerator();
        for (int i = 0; i < 5; i++) {
            executorService.execute(new PrimeNumberChecker(numberGenerator));
        }
        executorService.shutdown();
    }
}

与其在抽象class中具有“何时应该停止”的逻辑,不如让线程本身意识到这一点。所以我会在 Runnable class 中添加一个 AtomicBoolean 作为标志,并添加一个 stop() 方法来处理它。然后 运行 方法将寻找 AtomicBoolean 以在该布尔值更改时停止。

像这样:

public class PrimeNumberChecker implements Runnable {
    //Flag to control the running
    private final AtomicBoolean isRunning = new AtomicBoolean(false);

    private NumberGenerator generator;
    private Lock lock = new ReentrantLock();
    public Condition continueLock = lock.newCondition();
    
    public PrimeNumberChecker(NumberGenerator generator) {
        this.generator = generator;
    }
    
    @Override
    public void run() {     
       isRunning.set(true);
       while (isRunning.get()) {        
            int number = generator.generateNumber();
            System.out.println(Thread.currentThread().getName() + " generated " + number);
            if (check(number)) {
                System.out.println(number + " is prime !");
                generator.stop();
            }
    
        }
    
    }       
    
    // So you can stop it from the outside
    public void stop() {
        isRunning.set(false);
    }

    public static boolean check(int number) {
    
        boolean result = true;
        for (int i = 0; i <= number / 2; i++) {
            if ((number % 2) == 0) {
                result = false;
            }
        }
        return result;
    }
}

您可以将检查方法优化为:

public static boolean check(int number) {
    for (int i = 2; i <= number / 2; i++) {
        if ((number % i) == 0) {
            return false;
        }
    }
    return true;
}

只要你知道这个数字不是质数,你就可以早点 return。

When I find a prime number, I have to stop it. If I use it before synchronized while, only one thread process will occur. Multiple thread operations should occur, but should stop when prime is found.

您可以通过先将 volatile 添加到 isStop 标志来实现:

 private volatile boolean isStop = false;

然后在判断数字是否为素数的方法中检查 generator.isStopped()(也):

public boolean check(int number) {
    for (int i = 2; i <= number / 2; i++) {
        if (generator.isStopped() || number % i == 0) {
            return false;
        }
    }
    return true;
}

最后,需要同步读取check方法的值,因为可能会出现多个线程同时求素数的情况。因此,将您的代码调整为:

    boolean result = check(number); // All threads to work in parallel
    synchronized (generator) {
        if (result && !generator.isStopped()) {
            System.out.println(number + " is prime !");
            generator.stop();
        }
    }

Volatile 这里是不够的,因为多个线程可能设法进入

内的代码块内
if(result && !generator.isStopped())

在其中一个能够实际调用 generator.stop() 之前;。使变量 isStop AtomicBoolean 单独 也无济于事,原因完全相同。

关键是语句 !generator.isStopped()generator.stop(); 必须在同一个临界区内执行,要么使用同步,要么在同一个 go 中以原子方式执行这两个操作。因此,要使 AtomicBoolean 起作用,您必须执行以下操作:

public abstract class NumberGenerator {

    private final AtomicBoolean isStop = new AtomicBoolean(false);

    public abstract int generateNumber();

    public void stop() {
        this.isStop.set(true);
    }

    public boolean isStopped() {
        return isStop.get();
    }

    public boolean getAndSet(){
        return isStop.getAndSet(true);
    }
}

 if (check(number) && !generator.getAndSet()) {
       System.out.println(number + " is prime !");
   }

因为 getAndSet 是自动完成的,所以你不会 运行 有多个线程打印出它们的素数的风险。

What I want to do is to find prime numbers using lock and synchronized.

如果您的意思是只使用其中之一(因为您不需要同时使用两者),那么您可以执行以下操作:

    boolean result = check(number);
    synchronized (generator) {
        if (result && !generator.isStopped()) {
            System.out.println(number + " is prime !");
            generator.stop();
        }
    }

即使没有 volatile.

也能正常工作