【问题标题】:Atomic operation on read/write variable in javajava中读/写变量的原子操作
【发布时间】:2016-05-20 18:45:54
【问题描述】:

我有一个java类如下:

public class Example implements Runnable {

    private int num;

    ...

    // Getter
    public int getNum(){
        return this.num;
    }

    // Setter    
    public void addToNum(int amount) {

        if (this.amount> 0) {
            this.num += amount;
        }
    }

    ...
}

这个类可以被多个线程实例化。每个实例都有自己的“num”,也就是说,我不希望在所有实例之间共享“num”变量。

对于每个实例,可以同时访问多个线程以读取/写入“num”变量。那么保护“num”变量上的读/写操作以使其成为原子操作的最佳选择是什么?

我知道如果在 C# 上可以使用如下链接的 lock(object) 来完成,但在 java 中我不知道(我是新手):

Atomic operations on C#

【问题讨论】:

  • 制作setter方法addToNum同步
  • 您是说Example 实例不会在线程之间共享吗?因为如果是这种情况,那么您就可以了,您不需要添加任何同步或锁定。只有当两个(或更多)线程将访问同一个 Example 实例时,您才需要考虑它。
  • 希望您希望 addToNum() 一次只能由一个线程访问。你不同步那个方法吗?更好的方法是使用同步块
  • 因为 num 是私有的,所以不用担心同步
  • if (this.num > 0) { 正确吗?对于给定的代码,this.num 永远不会增加,因此不存在线程安全问题 :)

标签: java multithreading thread-safety locking atomic


【解决方案1】:

在 Java 中,我怀疑使用 volatile 变量,因为 volatile 变量只能在一个线程正在写入而其他读取正在读取时使用。 Volatile 仅在一个线程正在写入时起作用。

“其中一个线程 (T1) 修改计数器,而另一个线程 (T2) 读取计数器(但从不修改它),声明计数器变量 volatile 足以保证 T2 对计数器变量的写入可见性。

但是,如果 T1 和 T2 都在递增计数器变量,那么声明计数器变量 volatile 是不够的。稍后会详细介绍。”

链接:http://tutorials.jenkov.com/java-concurrency/volatile.html#:~:text=The%20Java%20volatile%20keyword%20is%20intended%20to%20address%20variable%20visibility,read%20directly%20from%20main%20memory.

【讨论】:

  • 有标记可用于将您的答案的一部分表示为引用的材料。您可以编辑答案并使用编辑器工具栏上的引号按钮或在段落前加上 > 符号。
【解决方案2】:

您可以同步方法,但您可能会发现使用 AtomicInteger 是一种更快的选择。

private final AtomicInteger num = new AtomicInteger();

...

// Getter
public int getNum(){
    return this.num.get();
}

// Setter    
public void addToNum(int amount) {
    if (amount > 0) {
        this.num.getAndAdd(amount);
    }
}

这两种方法都是无锁的,并且避免暴露可能以意外方式使用的锁。

在 Java 8 中,getAndAdd 使用单个机器代码指令通过 Unsafe 类进行添加。来自AtomicInteger

private volatile int value;

public final int get() {
    return value;
}
public final int getAndAdd(int delta) {
    return unsafe.getAndAddInt(this, valueOffset, delta);
}

【讨论】:

  • AtomicInteger 会比使用普通同步更快吗?
  • @TheLostMind 它比偏向锁定更快。即即使同步仅由一个线程实际使用。这是因为它使用单个机器代码指令(在 Java 8 中)
  • 啊,我明白了。会记得..谢谢..不能两次投票:P
  • @TheLostMind 你可以投票给任何你喜欢的 cmets ;) 我已经更新了我的答案。
【解决方案3】:

你可以使用 synchronized ,阅读一下。您可以同步方法。

【讨论】:

    【解决方案4】:
    public synchronized void addToNum(int amount) {
    
        if (this.num > 0) {
            this.num += amount;
        }
    }
    

    在这里你会找到它的文档 http://www.programcreek.com/2014/02/how-to-make-a-method-thread-safe-in-java/

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-08-12
      • 1970-01-01
      • 1970-01-01
      • 2011-02-13
      • 1970-01-01
      • 2012-03-06
      • 1970-01-01
      相关资源
      最近更新 更多