如何处理java并发中的陈旧数据?

时间:2017-01-03 03:40:25

标签: java concurrency

我正在关注这本书' 实践中的Java并发'详细了解Java Concurrency。然后我遇到了一个名为“陈旧数据'

的术语。

  

书中说:不充分同步的程序可能会导致意外   结果 - >陈旧数据。

本书中给出了一个例子,它使用' 同步'来保护mutator方法。关键字&使用' @GuardedBy '关于其领域的注释。我想过测试它。

import net.jcip.annotations.*;
public class ThreadTest4 extends Thread{

    private MyWork4 myWork= new MyWork4();

    public static void main(String[] args){
        ThreadTest4 thread1 = new ThreadTest4();
        ThreadTest4 thread2 = new ThreadTest4();
        ThreadTest4 thread3 = new ThreadTest4();
        ThreadTest4 thread4 = new ThreadTest4();

        thread1.start();
        thread2.start();
        thread3.start();
        thread4.start();

    }

    public void run(){
            myWork.setA();
            System.out.println(myWork.getA());
    }
}

class MyWork4{

    @GuardedBy("this")  private static int a;

    public synchronized int getA(){
        return a;   
    }

    public synchronized void setA(){
        a++;
    }

}

但结果却让我感到惊讶!!可能是什么原因?

enter image description here

1 个答案:

答案 0 :(得分:3)

你有两个问题。

首先,您锁定的myWork对象对每个线程都是私有的,因此在调用setA()时,线程只会互相锁定,这会尝试修改静态int,这需要锁定FIRST OBJECT以允许值更改。因此,除了Thread1之外,getA()调用都不依赖于等待锁定。

由此产生的第二个问题是你的set和get调用是重叠的。 @GuardedBy阻止项目被没有锁定的项目修改,并且同步方法只能由拥有锁定的调用者调用。所有线程都注册了setA()个调用,但必须等待锁修改该值。 Thread1以锁定开始,修改该值,然后释放锁定,然后通过getA()调用再次请求锁定。

然后,当Thread1释放锁时,执行Thread2的setA()调用。 Thread2在递增值后释放锁定,然后使用自己的getA()调用注册其锁定请求。

Thread1现在获取锁定以执行其等待getA()调用,并打印出2,因为此时,Thread1和Thread2已经修改了该值。

Thread3获取锁定并执行其等待setA()调用,并再次递增该值并释放锁定,并注册其getA()调用。

线程2然后获取其等待getA()调用的锁定并打印出3并释放锁。

接下来发生线程3的等待getA()调用,并再次输出3,因为还没有其他的setter调用。

最后,最后启动的Thread4开始运行,并注册其setA()调用递增值,然后打印出新增加的getA()调用,因为它不再等待锁定

您的run方法未同步,并且您的各个线程没有自行订购,除非首先请求锁定,这取决于基本上随机的足够的不同因素。

以下是一项修改,可让您的订单更具可预测性:

public class ThreadTest4 extends Thread {
    static Object lock = new Object[0];
    private MyWork4 myWork = new MyWork4();

    public static void main(String[] args) {
        ThreadTest4 thread1 = new ThreadTest4();
        ThreadTest4 thread2 = new ThreadTest4();
        ThreadTest4 thread3 = new ThreadTest4();
        ThreadTest4 thread4 = new ThreadTest4();

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

    public void run() {
        synchronized(lock){
            myWork.setA();
            System.out.println(myWork.getA());   
        }
    }
}

class MyWork4 {

    @GuardedBy("lock")
    private static int a;

    public synchronized int getA() {
        return a;
    }

    public synchronized void setA() {
        a++;
    }

}

这是有效的,因为锁是外部的并且在线程之间显式共享。所有线程都使用相同的锁,因此它们会在下一个线程被赋予锁之前按顺序执行setA()调用和getA()调用,这样可以让它们更好地播放。