【问题标题】:Prime Numbers Generating Threads in Java printing twice质数在 Java 打印中生成线程两次
【发布时间】:2018-04-10 17:14:13
【问题描述】:

我正在尝试打印一个点到另一个点之间的素数,比如说在一个线程中从 1 到 1000,在另一个线程中从 1000 到 2000,但是当我使用 foreach 循环打印每个线程时,它会给我一个无序的 Arraylist 打印出来两次。

我正在尝试使用两个并发线程打印 1、2、3、5、7...。请帮助我,以便我更好地理解线程。

public class PrimeNumberGenerator implements Runnable{

    protected long from, to;
    static ArrayList<Long> primeList = new ArrayList<Long>();

    public  PrimeNumberGenerator(long from,long to)
    {
        this.from = from;
        this.to = to;
    }

    public long count = 0;

    public void run() {
        for(long n=from; n<=to; n++){
            boolean isPrime = true;
            for(long i = 2; i<n; i++) {
                if(n % i==0) {
                    isPrime = false;
                    break;
                }
            }
            if(isPrime) {
                count++;
                primeList.add(n);
            }
        }
    }

    public ArrayList<Long> getPrimes() {
        return primeList;
    }

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        PrimeNumberGenerator gen1 = new PrimeNumberGenerator(1L,1000L);
        PrimeNumberGenerator gen2 = new PrimeNumberGenerator(1001L,2000L);
        Thread t1 = new Thread(gen1);
        Thread t2 = new Thread(gen2);
        t1.start();

        t2.start();
        t1.join();
        t2.join();
        gen1.getPrimes().forEach(primeList -> System.out.println(primeList));
        gen2.getPrimes().forEach(primeList -> System.out.println(primeList));
    }
}

【问题讨论】:

  • 你在两个循环中调用 system out printline。
  • 由于 ArrayList 是静态的,因此两个线程都在添加。当您调用 System.out.println 时,您将打印同一个线程两次。如果顺序很重要,我建议使用 TreeSet,它将被订购。
  • 不会使用每个对象打印每个线程然后再打印第二个吗?
  • @JainamShah 由于列表是静态的,因此两个线程都使用它的相同实例。
  • 此外,它们以非线程安全的方式访问同一个实例。你很幸运,没有看到异常或数据损坏。

标签: java multithreading


【解决方案1】:

问题是您有两个线程同时填充相同的ArrayList,因为您的ArrayListstatic(意味着整个应用程序中只会共享一个实例)

第一个线程可能添加三个数字,然后第二个线程添加三个数字,然后再次添加第一个,导致ArrayList包含

[1, 2, 3, 1009, 1013, 1019, 5, 7, 11]

最后你(正确地)等待Threads 完成并打印相同的(错误排序)ArrayList 两次!

只需使您的ArrayList 非静态,它就会起作用,这样PrimeNumberGenerator 将拥有自己的ArrayList

【讨论】:

【解决方案2】:

我建议使用 TreeSet 来保持素数有序。 TreeSet 需要正确同步以进行多线程访问。

public class PrimeNumberGenerator implements Runnable {

    protected long from, to;
    static Set<Long> primeList = new TreeSet<Long>();

    public PrimeNumberGenerator(long from, long to)
    {
        this.from = from;
        this.to = to;
    }


    public long count=0;

    public void run() {
        for(long n=from;n<=to;n++) {
            boolean isPrime = true;
            for(long i = 2; i<n; i++) {
                if(n % i==0) {
                    isPrime = false;
                    break;
                }
            }
            if(isPrime) {
                count++;
                synchronized(primeList) {
                   primList.add(n);
                }
            }
        }
    }

    public static ArrayList<Long> getPrimes(){
        //Make a copy so we don't need to synchronize outside of this class
        return new ArrayList<>(primeList);
    }

    public static void main(String[] args) throws Exception {
        // TODO Auto-generated method stub
        PrimeNumberGenerator gen1 = new PrimeNumberGenerator(1L,1000L);
        PrimeNumberGenerator gen2 = new PrimeNumberGenerator(1001L,2000L);
        Thread t1 = new Thread(gen1);
        Thread t2 = new Thread(gen2);
        t1.start();

        t2.start();
        t1.join();
        t2.join();
        PrimeNumberGenerator.getPrimes().forEach(primeList -> System.out.println(primeList));

    }
}

【讨论】:

  • 如果你让它非静态你不需要使用TreeSet
  • 好吧,我把它拿出来。
  • 如果你让它线程安全,让他们分享一个TreeSet实际上会更好(虽然不是静态的)这个答案会得到我的+1;)
  • 当有两个线程在同一个对象上运行时,您经常会遇到问题。如果您使用并发,您应该首先认真阅读一个好的教程!并发比您想象的要复杂得多
  • @LonelyNeuron 我更新了我的答案,以便解决线程安全选项。
猜你喜欢
  • 1970-01-01
  • 2018-01-25
  • 1970-01-01
  • 1970-01-01
  • 2013-05-17
  • 1970-01-01
  • 1970-01-01
  • 2020-05-21
  • 1970-01-01
相关资源
最近更新 更多