【问题标题】:Java thread safe counter own lock implementationJava线程安全计数器自己的锁实现
【发布时间】:2015-12-13 19:47:51
【问题描述】:

我只是想在从不同线程修改变量时如何锁定变量,例如 int。我不会那样使用它,而且我知道 Java 中的 synchronized 关键字,但我想了解它以及它一般是如何工作的。

这就是我的代码:

private int i = 0;
private int lock = 0;

public void count(int lock) {
    while (true) {
        if (this.lock == 0) {
            this.lock = lock;
        }

        if (this.lock == lock) {
            i ++;
            this.lock = 0;
            return;
        }
    }
}

问题:它有效吗?这个计数器线程安全吗?如果是: synchronized 关键字是否有类似的工作?如果没有:为什么不呢?

编辑:我忘了说每个线程调用 count 方法时使用不同的锁值。

【问题讨论】:

  • AtomicInteger 是你的朋友。这看起来根本不是一个好主意......(一方面,不清楚您期望该方法的 lock 参数是什么......)
  • 我刚刚编辑了这个问题。每个线程使用不同的锁参数
  • 由于很多原因它不会工作......
  • 嗯,那里基本上没有内存屏障,所以看起来一点也不安全。两个线程完全有可能各自看到他们刚刚写入的lock 值,然后不安全地更新i。从根本上说,除非您是成熟的 Java 内存模型专家(我当然不是),否则尝试提出您自己的像这样的锁定原语是一个非常糟糕的主意。 AtomicInteger 确实是要走的路...或使用单独的Object 进行同步。
  • @JonSkeet 这就是我检查两次的原因。

标签: java thread-safety locking counter synchronized


【解决方案1】:

简短的回答是“不”,那里不能保证线程安全。

更长的答案是您需要查看Java Memory Model 以了解原因。部分问题是不同的线程可能不会同时看到变化。内存模型仅保证写入后的读取在执行写入的同一线程中看到更新的值,除非跨越内存屏障。另一个线程可能看不到该更新,即使它在另一个线程执行写入之后执行读取。除非您使用 volatile 关键字描述变量。或者使用synchronized 关键字来确保只有一个线程在更新值。这种行为允许 Java 运行时优化代码,也许通过内联它注意到的东西在循环中是恒定的,并且还可以使用本地汇编语言,提供同步性保证,但明显更快。

问题的另一部分是,当两个更新同时发生在不同线程上时,确保两个线程都可以使用相同结果的唯一方法是使用相应的低级汇编语言指令 -这可能被称为 compareAndSet、compareAndSwap 或 testAndSet。

因此,就您编写的代码而言,完全可以实现相当于这种执行顺序(其中 T1 和 T2 作为单独的线程)...

// assuming i begins at 0.
T1: if this.lock == true
T2: if this.lock == true
T1: this.lock = lock
T1: if this.lock == lock     // evaluates to true
T2: if this.lock == lock     // evaluates to true
T1: this.lock = lock
T2: this.lock = lock

它实际上更复杂,因为你不能说这些指令中的每一个都是 CPU 上的一条指令。从根本上说,您可以让两个线程同时执行某种更新,所有随之而来的结果都围绕着双重或丢失更新。

更简单的答案:使用java.util.concurrent 中提供的原子原语作为线程安全计数器。

【讨论】:

    猜你喜欢
    • 2012-11-20
    • 1970-01-01
    • 1970-01-01
    • 2015-07-05
    • 2018-03-08
    • 1970-01-01
    • 2018-09-27
    • 2013-06-03
    • 2013-05-30
    相关资源
    最近更新 更多