简体   繁体   English

了解 Java 线程干扰

[英]Understanding Java Thread Interference

I'm learning Java, coming from a python background, and trying to understand thread interference, starting with the code & explanation in this page: http://docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html我正在学习 Java,来自 python 背景,并试图理解线程干扰,从本页中的代码和解释开始: http : //docs.oracle.com/javase/tutorial/essential/concurrency/interfere.html

To reproduce interference, I have another class that starts three threads, each randomly calling either increment or decrements 10 times.为了重现干扰,我有另一个类启动三个线程,每个线程随机调用增量或减量 10 次。

I expect, with 3 threads & 30 increments or decrements, some would overlap, and thus the final Counter value would not equal the (# increments) - (# decrements) .我希望,有 3 个线程和 30 个增量或减量,有些会重叠,因此最终的Counter值将不等于(# increments) - (# decrements)

But every time I run the code and analyze the resulting output, I find the final value to equal (# increments) - (# decrements) .但是每次我运行代码并分析结果输出时,我都会发现最终值等于(# increments) - (# decrements) Although its possible that after 5 runs, I somehow did not get any interference, its much more likely that I misunderstood the interference effect or inadvertently implemented code that avoids interference.虽然有可能在运行 5 次后,我不知何故没有受到任何干扰,但更有可能是我误解了干扰效应或无意中实现了避免干扰的代码。

Here's my code:这是我的代码:

// 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();
        }
    }
}

The file Counter.java contains code copied from the oracle documentation above, reproduced here for convenience文件Counter.java包含从上面的 oracle 文档中复制的代码,为了方便起见,复制到这里

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

    void increment ()
    {
        c++;
    }

    void decrement()
    {
        c--;
    }

    int value()
    {
        return c;
    }
}

To reproduce, you need to maximize the probability to increment and/or decrement your counter concurrently (NB: it is not an easy task since incrementing/decrementing a counter is a very fast operation), which is not the case of your current code because:要重现,你需要的概率最大限度地增加和/或同时递减的计数器(注:这是不是因为递增/递减计数器是一个非常快速的操作一件容易的事),这是不是你的当前代码的情况下,因为:

  1. You don't use any mechanism to synchronize the threads before incrementing/decrementing your counter.在增加/减少计数器之前,您不使用任何机制来同步线程。
  2. You print your messages in the standard output stream too often and at the wrong place which is a problem when you know that a PrintStream is thread-safe and uses an intrinsic lock to prevent concurrent accesses which reduces the probability to increment and/or decrement your counter concurrently.您在标准输出流中过于频繁地在错误的地方打印您的消息,当您知道PrintStream是线程安全的并使用内在锁来防止并发访问时,这会导致问题,这降低了增加和/或减少您的同时反。
  3. You add a useless long sleep which once again reduces the probability to get concurrent modifications of your counter.您添加了一个无用的长时间睡眠,这再次降低了同时修改计数器的可能性。
  4. You don't use as many threads as you can.您不会使用尽可能多的线程。

So, your code should be rewritten a little bit to fix the previous problems.因此,您的代码应该稍微重写一下以解决之前的问题。

To fix #1, you can use a CyclicBarrier to make sure that all your threads reach the same barrier point (located just before incrementing/decrementing your counter) before going any further.要修复 #1,您可以使用CyclicBarrier来确保所有线程在继续之前到达相同的障碍点(位于递增/递减计数器之前)。

To fix #2, I would recommend to keep only one message after having incremented/decremented your counter.为了解决#2,我建议增加/减少计数器只保留一条消息。

To fix #3, I would simply remove it as it is useless anyway.要修复 #3,我会简单地将其删除,因为它无论如何都没用。

To fix #4, I would use Runtime.getRuntime().availableProcessors() as amount of threads to use since it will use as many processors as you have on your local machine which should be enough for such kind of task.为了修复 #4,我将使用Runtime.getRuntime().availableProcessors()作为要使用的线程数量,因为它将使用与您在本地机器上一样多的处理器,这对于此类任务来说应该足够了。

So the final code could then be:所以最终的代码可能是:

Counter柜台

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();
    }
    ...
}

The main method main方法

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

The for loop of the run method 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