Des résultats inattendus dans les discussions Java multi-malgré les verrous


Yaniv:

Je suis aux prises avec un code d'exercice qui devrait calculer la somme des carrés utilisant des fils. Pour une raison quelconque, je reçois des résultats incohérents malgré l'utilisation de serrures, se sont également assurés que je suis verrouillage sur des objets et non objets locaux / variables etc.

Lorsque j'ajouter la connexion à comprendre le comportement, le code fonctionne beaucoup plus lent et exécute très bien. en va de même lorsque l'on réduit à un fil. Mais - une fois que je lance plusieurs threads il agir de façon imprévisible. Le résultat devrait être

Mon code:

    package com.yaniv.concurrency;

import java.sql.Timestamp;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class SumSquaresSync implements Runnable {

    ReentrantLock nextLock = new ReentrantLock();
    ReentrantLock sumLock = new ReentrantLock();

    Long sum = new Long(0);
    int min, max;
    Integer next = new Integer(0);
    int threadBatchSize = 10;

    static final boolean LOG = false;

    @Override
    public void run() {
        long localSum = 0;
        int[] batch = new int[threadBatchSize];

        while (next <= max) {
            nextLock.lock();                
            if (this.next <= max) {
                {
                    for (int i = 0; i < threadBatchSize; i++) {
                        batch[i] = ((next + i <= max) ? (next + i) : 0);
                    }
                    next += threadBatchSize;
                }
            }
            nextLock.unlock();


            if (LOG) {
                synchronized (System.out) {
                    System.out.print(Thread.currentThread().getName() + " got batch " + batch.toString() + ": ");
                    for (int i = 0; i < threadBatchSize; i++) {
                        System.out.print(batch[i] + ", ");
                    }
                    System.out.println();
                }
            }

            for (int i : batch) {
                localSum += Math.pow(i, 2);
            }
        }

        sumLock.lock();
        sum += localSum;
        sumLock.unlock();

        if (LOG) {
            safePrintln(Thread.currentThread().getName() + " terminated, localSum = " + localSum);
        }
    }

    private long executeSumSquares(int min, int max, int numberOfThreads, int threadBatchSize) throws Exception {
        this.min = min;
        this.max = max;
        this.next = min;
        this.threadBatchSize = threadBatchSize;
        this.sum = 0L;

        ExecutorService executorService = Executors.newFixedThreadPool(numberOfThreads);

        for (int i = 0; i < numberOfThreads; i++) {
            if (LOG) {
                System.out.format("Adding thread  %d%n", i);
            }
            executorService.execute(new SumSquaresSyncThread(this));
        }

        executorService.shutdown();
        executorService.awaitTermination(5_000L, TimeUnit.MILLISECONDS);

        return sum;

    }

    public static void main(String[] args) throws Exception {
        SumSquaresSync SumSquaresSync = new SumSquaresSync();
        long total;
        int iteration = 0;
        Timestamp startTime, endTime;

        do {
            iteration++;
            startTime = new Timestamp(System.currentTimeMillis());
            total = SumSquaresSync.executeSumSquares(1, 10000, 1, 5);
            endTime = new Timestamp(System.currentTimeMillis());

            System.out.println("==========================================");
            System.out.format("Total sum: %,8d, elapsed time %d, iteration %d%n", total, (endTime.getTime() - startTime.getTime()), iteration);
            System.out.println("==========================================");

        } while (iteration < 10);  //(total == 333383335000L);


    }

    public void safePrintln(String s) {
        synchronized (System.out) {
            System.out.println(s);
        }
    }

    public void safePrint(String s) {
        synchronized (System.out) {
            System.out.print(s);
        }
    }

}

Les resultats:

Total sum: 347,938,671,335, elapsed time 16, iteration 1
Total sum: 342,283,818,850, elapsed time 10, iteration 2
Total sum: 336,257,779,565, elapsed time 10, iteration 3
Total sum: 336,233,345,285, elapsed time 9, iteration 4
Total sum: 337,663,242,000, elapsed time 8, iteration 5
Total sum: 336,779,784,290, elapsed time 10, iteration 6
Total sum: 335,474,886,225, elapsed time 10, iteration 7
Total sum: 338,825,524,135, elapsed time 8, iteration 8
Total sum: 335,820,751,880, elapsed time 10, iteration 9
Total sum: 335,083,150,300, elapsed time 8, iteration 10

Le résultat correct doit être 333.383.335.000. Quelqu'un peut-il dire ce que je suis absent?

CherryDT:

(Copié de commentaire)

Votre while (next <= max)chèque est pas protégé par une serrure.

Vous vérifiez à nouveau à l' intérieur de la serrure probablement pour cette raison, mais la for (int i : batch) { localSum += Math.pow(i, 2); }partie n'est pas dans la condition intérieure de sorte que le localSumpeut obtenir un lot ajouté deux fois parfois.

Déplacer l' for (int i : batch)intérieur de la boucle if (this.next <= max).

Articles connexes