繁体   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