簡體   English   中英

了解 Java 線程干擾

[英]Understanding Java Thread Interference

我正在學習 Java,來自 python 背景,並試圖理解線程干擾,從本頁中的代碼和解釋開始: http : //docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html

為了重現干擾,我有另一個類啟動三個線程,每個線程隨機調用增量或減量 10 次。

我希望,有 3 個線程和 30 個增量或減量,有些會重疊,因此最終的Counter值將不等於(# increments) - (# decrements)

但是每次我運行代碼並分析結果輸出時,我都會發現最終值等於(# increments) - (# decrements) 雖然有可能在運行 5 次后,我不知何故沒有受到任何干擾,但更有可能是我誤解了干擾效應或無意中實現了避免干擾的代碼。

這是我的代碼:

// file: CounterThreads.java
public class CounterThreads {
    private static class CounterThread implements Runnable {
        private Counter c;

        CounterThread(Counter c)
        {
            this.c = c;
        }

        public void run()
        {
            String threadName = Thread.currentThread().getName();
            for (int i=0; i<10; i++) {
                try {

                    if (((int)(Math.random() * 10) % 2) == 0) {
                        System.out.format("%s - Decrementing...\n", threadName);
                        c.decrement();
                    } else {
                        System.out.format("%s - Incrementing...\n", threadName);
                        c.increment();
                    }
                    System.out.format("%s - The internal counter is at %s\n", threadName, c.value());
                    Thread.sleep(1000);

                } catch (InterruptedException e) {
                    System.out.format("Thread %s interrupted\n", threadName);
                }
            }
        }
    }

    public static void main(String[] args)
    {
        Counter c = new Counter();
        for (int  i=0; i<3; i++) {
            Thread t = new Thread(new CounterThread(c));
            System.out.format("Starting Thread: %s\n", t.getName());
            t.start();
        }
    }
}

文件Counter.java包含從上面的 oracle 文檔中復制的代碼,為了方便起見,復制到這里

// file: Counter.java
public class Counter {
    private int c = 0;

    void increment ()
    {
        c++;
    }

    void decrement()
    {
        c--;
    }

    int value()
    {
        return c;
    }
}

要重現,你需要的概率最大限度地增加和/或同時遞減的計數器(注:這是不是因為遞增/遞減計數器是一個非常快速的操作一件容易的事),這是不是你的當前代碼的情況下,因為:

  1. 在增加/減少計數器之前,您不使用任何機制來同步線程。
  2. 您在標准輸出流中過於頻繁地在錯誤的地方打印您的消息,當您知道PrintStream是線程安全的並使用內在鎖來防止並發訪問時,這會導致問題,這降低了增加和/或減少您的同時反。
  3. 您添加了一個無用的長時間睡眠,這再次降低了同時修改計數器的可能性。
  4. 您不會使用盡可能多的線程。

因此,您的代碼應該稍微重寫一下以解決之前的問題。

要修復 #1,您可以使用CyclicBarrier來確保所有線程在繼續之前到達相同的障礙點(位於遞增/遞減計數器之前)。

為了解決#2,我建議增加/減少計數器只保留一條消息。

要修復 #3,我會簡單地將其刪除,因為它無論如何都沒用。

為了修復 #4,我將使用Runtime.getRuntime().availableProcessors()作為要使用的線程數量,因為它將使用與您在本地機器上一樣多的處理器,這對於此類任務來說應該足夠了。

所以最終的代碼可能是:

櫃台

public class Counter {
    private final CyclicBarrier barrier;
    private int c;

    public Counter(int threads) {
        this.barrier = new CyclicBarrier(threads);
    }

    void await() throws BrokenBarrierException, InterruptedException {
        barrier.await();
    }
    ...
}

main方法

public static void main(String[] args) {
    int threads = Runtime.getRuntime().availableProcessors();
    Counter c = new Counter(threads);
    for (int  i=0; i<threads; i++) {
        ...
    }
}

run方法的for循環

try {
    // Boolean used to know if the counter has been decremented or not
    // It has been moved before the await to avoid doing anything before
    // incrementing/decrementing the counter
    boolean decrementing = (int)(Math.random() * 10) % 2 == 0;
    // Wait until all threads reach this point
    c.await();
    if (decrementing) {
        c.decrement();
    } else {
        c.increment();
    }
    // Print the message
    System.out.format(
        "%s - The internal counter is at %d %s\n", 
        threadName, c.value(), decrementing ? "Decrementing" : "Incrementing"
    );

} catch (Exception e) {
    System.out.format("Thread %s in error\n", threadName);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM