再现线程干扰

时间:2012-07-08 01:42:30

标签: java multithreading

我正在尝试重现线程干扰场景..但有些事情是不对的。请帮我理解一下

主要

public static void main(String args[]) throws InterruptedException {

    Counter c = new Counter();

    for (int i = 0; i < 1000000; i++) {
        new T1(c).start();
        new T2(c).start();
    }

    System.out.println(c.value()); // <-- Expect this to sometimes not be 0
}

计数器

class Counter {
    private int c   = 0;

    public void increment() { // <-- intentionally not synchronized 
        c++;
    }

    public void decrement() { // <-- intentionally not synchronized 
        c--;
    }

    public int value() {
        return c;
    }
}

线程1

public class T1 extends Thread {

    Counter c;

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

    public void start() {
        c.decrement(); // <-- Decrement
    }
}

线程2

public class T2 extends Thread {

    Counter c;

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

    public void start() {
        c.increment();  // <-- Increment
    }
}

当我启动1000000个线程时,每个线程都运行在非同步代码段上,我希望某些操作重叠。

  

两个操作发生干扰,运行不同   线程,但作用于相同的数据,交错。这意味着   两个操作由多个步骤和步骤序列组成   重叠。

     

对Counter实例的操作似乎不太可能   interleave,因为对c的两个操作都是单个简单的语句。   但是,即使是简单的语句也可以转换为多个步骤   虚拟机。我们不会检查虚拟的具体步骤   机器需要 - 知道单个表达式c ++就足够了   可以分解为三个步骤:

     

检索c的当前值。将检索的值增加1。   将增加的值存储回c。

我缺少什么?

2 个答案:

答案 0 :(得分:4)

由于你的程序实际上并没有同时执行任何操作,所以一切都在主线程中完成。由于您重写了start方法并且从不调用超类版本,因此您无法获得使新线程运行的功能。

但是只是调用super.start()不会解决问题。考虑start是由当前线程运行的,所以即使你设法启动线程,增量/减量也将由调用start()的线程执行,并且没有并发访问和修改。

我将递增/递减的东西移出start方法(删除它的重写版本)并覆盖run方法,而不是将其放在那里。当然,考虑下次使用Runnable而不是Thread,使用Runnable会更难产生这种错误。

答案 1 :(得分:2)

您的程序存在很多问题,首先是您没有启动任何线程。对于线程,线程的主体必须在run()方法中。不能覆盖Thread类中的start()方法。

然后,您将创建的线程数量存在很大问题:2000000。这意味着2000000个堆栈要运行它们。默认堆栈大小(除非您使用-Xss选项指定它)介于512Kb和2Mb之间。即使你使用128Kb的低堆栈大小,你需要256GB的内存来创建这2000000个线程。好吧,如果你的线程不那么短暂,你需要那么多的内存。他们只做一个工作指令然后停止,所以可能大多数工作指令会在你开始太多工作之前终止。或者您的进程将因内存或资源不足而崩溃。最后,大部分时间都是在初始化和终止线程。我估计不到1%的线程使用的实际CPU用于计数器的实际递增或递减。这也意味着在操作中看到任何重叠是不太可能的。

我认为如果你真的想看到重叠,你想要创建更少的线程(IMO 16或更少。任何方式,只有这么多可以同时运行,即你的机器上的处理器核心数),但这些线程应该在循环中进行增量/减量。

所以有两种类型的线程,一种类型在计数器上增加200000,另一种减少200000次。启动每种类型的8个线程。使用Thread.join()等待所有16个线程的结束,然后写入计数器的结果。