【问题标题】:How to stop threads when one of them finds a prime number当其中一个找到素数时如何停止线程
【发布时间】:2021-04-06 02:01:35
【问题描述】:

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

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

我想做的是使用锁定和同步找到素数。

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();
    }
}

【问题讨论】:

    标签: java multithreading concurrency parallel-processing synchronization


    【解决方案1】:

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

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

    一旦你知道这个数字不是质数,你就可以早点返回。

    当我找到一个质数时,我必须停止它。如果我以前使用它 同步时,只会发生一个线程进程。多种的 线程操作应该发生,但应该在找到素数时停止。

    您可以通过将第一个 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(); 必须在同一个关键区域内执行,要么使用同步,要么在同一个过程中原子地执行这两个操作。因此,要让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 是以原子方式完成的,所以您不会冒让多个线程打印出它们的素数的风险。

    我想做的是使用 lock 和 同步。

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

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

    即使没有 volatile,这也可以工作。

    【讨论】:

      【解决方案2】:

      与其在抽象类中具有“何时应该停止”的逻辑,线程本身应该知道它。所以我会在 Runnable 类中添加一个 AtomicBoolean 作为标志,并添加一个 stop() 方法来处理它。然后 run 方法将寻找 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;
          }
      }
      

      【讨论】:

      • 当我尝试这个过程时。它再次给出了相同的结果。当它是质数时,它并没有停止。
      • 您好 Fransisco,您当前解决方案的问题是标志 isRunning 是每个线程私有的。
      • 是的@dreamcrash,变量是私有的,但是有一个公共方法“stop”来处理它。
      • 该方法是公共的,但每个线程都有自己的私有变量,它们不共享相同的内存引用
      • 你说得对,我认为每个线程一个就足够了,但也许他想停止所有线程,而不是每个线程。好收获!
      猜你喜欢
      • 2015-12-29
      • 1970-01-01
      • 2021-07-05
      • 2021-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-29
      相关资源
      最近更新 更多