2没有synchronized关键字的线程访问共享资源,仍然可以正常工作

时间:2015-05-06 03:52:20

标签: java multithreading

public class Worker {
private int count = 0;                  //shared resource

public static void main(String[] args) {
    Worker w = new Worker();
    w.doWork();
}

public void doWork() {

    Thread thread1 = new Thread(new Runnable() {   //Thread incrementing count 10000 times
        public void run() {
            for (int i = 0; i < 10000; i++) {
                count++;          //Not Atomic operation

            }

        }
    });

    Thread thread2 = new Thread(new Runnable() {    //Thread incrementing count 10000 times
        public void run() {
            for (int i = 0; i < 10000; i++) {
                count++;           //Not Atomic operation
            }

        }
    });
    thread1.start();
    thread2.start();

    try {//halts main thread so that both thread race to increment count
        thread1.join();
        thread2.join();
    } catch (InterruptedException e) {
        // TODO Auto-generated catch block
        e.printStackTrace();
    }

    System.out.println("Count is: " + count); 
}
}

我从Cave Of Programming复制了这段代码。 每次运行此代码时,我都会将结果显示为20000而不使用synchronized关键字,这是意外的。 代码是否正确? 我的操作系统正确安排了两个线程? (我知道这很奇怪问题)

每次运行代码时,我都期待不必要的结果。

6 个答案:

答案 0 :(得分:1)

首先,count ++不是原子的。 其次,虽然你的答案是正确的2000,但我们不能保证它总是正确的。

原因如下。操作计数++被视为一个3步骤过程

 1. Fetch the count value
 2. Increment the count value
 3. assign the incremented value back to count variable

因此,当thread1正在读取时,线程2可能会增加该值。仍然线程1具有ole值,并且覆盖了thread2

所包含的值

如果你使count ++原子,我肯定是正确的。为此,您可以使用如下

 AtomicInteger count = new AtomicInteger();

你可以使用

增加它
 count.incrementAndGet();

答案 1 :(得分:1)

根据操作系统和系统负载,它的行为会有所不同。

我执行了相同的代码3次,每次都得到不同的结果

  

$ java工作人员   数量是:10437
  $ java工作人员   数量是:10395
  $ java工作人员   数量是:10684

我使用Ubuntu 14(x64)和OpenJDK-7

答案 2 :(得分:0)

预期输出是两个线程都将count变量从0递增10000,因此预期输出确实为0 + 10000 + 10000 = 20000。

但是,由于count ++不是原子操作,因此这不是确定性的。再运行一次,你可能得到不同的结果。

答案 3 :(得分:0)

代码不正确。正确的结果是巧合。

首先计算++不是原子的。

其次,任务可能会一直运行直到结束,另一个任务可以在之后运行 - 这里没有限制。时间表可以决定给予一个任务实际完成的时间,而不是切换,或者它只是提供足够的时间来计算++实际上表现得像原子(仅仅因为交换机之间的时间)。但是可能会提供不同的结果。最终,2000年只是一个幸福的巧合。

尝试增加循环大小,以便查看不同的结果。 总之,代码是正确的,以提供不正确的结果,如您所料,只需扩展实验。

答案 4 :(得分:0)

根据你的代码,你的for循环将在两个套装中执行10000次,所以很明显它会给你20000个结果。

建议:尝试打印 window.oncontextmenu = function (event) { event.preventDefault(); event.stopPropagation(); return false; };

的中间值
count

然后,您将意识到与Thread thread1 = new Thread(new Runnable() { //Thread incrementing count 10000 times public void run() { for (int i = 0; i < 10000; i++) { count++; //Not Atomic operation System.out.println(count); } } }); synchronized的区别。

答案 5 :(得分:0)

@Javed ..我希望你已经看到了线程中的连接基础.. 看起来Join正在为你和我工作...... 我已经运行了你的程序10次,总是有20000。 我之前尝试过一个SOP,比如Thread.currentThread()。getName() 两个线程中的count ++然后运行程序。 我发现thread1之间没有交错 和thread2 ...然后显示Thread1的所有SOP 显示Thread2的SOP,因此没有比赛.. Thread1完成它的工作,然后Thread2完成它的工作 因此20000将永远是结果。