Search code examples
javamultithreadingblockingqueue

Finding prime number using BlockingQueue in Java with multi threading


I've implemented a multi thread program using BlockingQueue to test for a number is prime or not.

The requirement is the program will prompt user to input a number and then the program will print every prime number from 2 to inputted number from user in ascending order.

I have 3 class: NumberEnumerationTask for initialize BlockingQueue contains all numbers to be check, PrimeRunner for check the number is prime or not, PrimeChecker for the main class.

NumberEnumerationTask.java

package multithread;

import java.util.concurrent.BlockingQueue;

public class NumberEnumerationTask implements Runnable {
    private BlockingQueue<Integer> queue;
    private Integer maximum;
    public static Integer DUMMY = new Integer(0);

    public NumberEnumerationTask(BlockingQueue<Integer> queue, Integer maximum) {
        this.queue = queue;
        this.maximum = maximum;
    }

    @Override
    public void run() {
        try {
           enumerate();
           queue.put(DUMMY);
        } catch (InterruptedException e) {
           e.printStackTrace();
        }
    }

    /**
    * Create a BlockingQueue contain Integer number from 1 to maximum.
    * @throws InterruptedException
    */
    private void enumerate() throws InterruptedException {
        for (int i = 2; i < maximum; i++) {
           queue.put(i);
        }
    }
}

PrimeRunner.java

package multithread;

import java.util.concurrent.BlockingQueue;

public class PrimeRunner implements Runnable {

    private BlockingQueue<Integer> queue;

    public PrimeRunner(BlockingQueue<Integer> queue) {
        this.queue = queue;
    }

    @Override
    public void run() {
        try {
            boolean done = false;
            while (!done) {
                Integer checkNumber = queue.take();
                if (checkNumber == NumberEnumerationTask.DUMMY) {
                    queue.put(checkNumber);
                    done = true;
                } else {
                    checkPrimeNumber(checkNumber);
                }
            }
        } catch(InterruptedException e) {
            e.printStackTrace();
        }
    }

    private void checkPrimeNumber(Integer number) {
        boolean isPrime = true;
        for (int i = 2; i <= number / 2; i++) {
            if (number % i == 0) {
                isPrime = false;
                queue.remove(number);
                break;
            } 
        }
        if (isPrime == true) {
            System.out.print(number + " ");
            queue.remove(number);
        }
    }
}

PrimeChecker.java

public class PrimeChecker {

    public static void main(String[] args) {
    
        Scanner sc = new Scanner(System.in);
        System.out.print("Enter maximum number to check: ");
        Integer number = sc.nextInt();
    
        BlockingQueue<Integer> queue = new ArrayBlockingQueue<Integer>(number);
    
        NumberEnumerationTask initialize = new NumberEnumerationTask(queue, number);
        new Thread(initialize).start();
    
        for (int i = 0; i <= number; i++) {
            new Thread(new PrimeRunner(queue)).start();
        }
    
        sc.close();
    }
}

The DUMMY variable is to signal completion.

When I run the program, sometime it's not print in ascending order, sometime it does.

Enter maximum number to check: 100

2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97

Enter maximum number to check: 100

2 3 5 7 11 13 17 19 23 29 31 41 43 47 37 59 61 53 67 71 73 79 83 89 97

Can someone tell me what is wrong with my code? Thanks


Solution

  • In PrimeChecker, the following code is the cause:

    for (int i = 0; i <= number; i++) {
        new Thread(new PrimeRunner(queue)).start();
    }
    

    You create multiple PrimeRunner instances and a thread for each of them, so something like below can happen:

    1. PrimeRunner in thread 1 takes 5.
    2. PrimeRunner in thread 2 takes 7.
    3. PrimeRunner in thread 2 prints 7. (PrimeRunner in thread 1 is still checking...)
    4. PrimeRunner in thread 1 prints 5.

    If you run a single PrimeRunner you can avoid such cases and it works as you expected. If you still want to run multiple PrimeRunner surrounding the following part by synchronized block with an object in a static field of PrimeRunner should solve the problem:

    Integer checkNumber = queue.take();
    if (checkNumber == NumberEnumerationTask.DUMMY) {
        queue.put(checkNumber);
        done = true;
    } else {
        checkPrimeNumber(checkNumber);
    }