如果静态int线程在单个方法中递增,那么它是否安全?

4 java multithreading thread-safety

我想跟踪getVariableAndLogAccess(RequestInfo requestInfo)使用下面的代码.如果只有这两个方法访问变量,它是否是线程安全的?使线程安全的标准方法是什么?

public class MyAccessLog(){

    private int recordIndex = 0;
    private int variableWithAccessTracking = 42; 
    private final Map<Integer, RequestInfo> requestsLog = new HashMap<>();
    public int getVariableAndLogAccess(RequestInfo requestInfo){
        Integer myID = recordIndex++;
        int variableValue = variableWithAccessTracking;
        requestInfo.saveValue(variableValue);
        requestLog.put(myID, requestInfo);
        return variableValue;
     }
     public void setValueAndLog(RequestInfo requestInfo, int newValue){
        Integer myID = recordIndex++;
        variableWithAccessTracking = variableValue;
        requestInfo.saveValue(variableValue);
        requestLog.put(myID, requestInfo);
     }

/*other methods*/
}
Run Code Online (Sandbox Code Playgroud)

Mal*_*alt 10

如果只有这两个方法访问变量,它是否是线程安全的?

没有.

例如,如果两个线程调用setValueAndLog,它们可能最终具有相同的myID值.

使线程安全的标准方法是什么?

您应该intAtomicInteger替换,使用syncrhonized块来防止并发修改.

根据经验,使用如前所述的原子变量AtomicInteger比使用锁更好,因为锁涉及操作系统.调用操作系统就像引入律师一样 - 最好避免使用自己能够解决的问题.

请注意,如果使用锁或同步块,则setter和getter都需要使用相同的锁.否则,在setter仍在更新变量时可以访问getter,从而导致并发错误.


Ste*_*n C 5

如果仅这两个方法访问变量,它将是线程安全的吗?

不。

直观地讲,有两个原因:

  • 增量包括读取和写入。JLS不保证两者将作为原子操作执行。确实,Java实现都没有这样做。

  • 现代多核系统通过快速的本地内存高速缓存和较慢的主内存来实现内存访问。这意味着一个线程不能保证看到另一个线程的内存写入结果...除非有适当的“内存屏障”指令来强制执行主内存写入/读取。

    Java仅在内存模型认为有必要时才插入这些指令。(因为...他们放慢了代码速度!)

从技术上讲,JLS有一整章描述了Java内存模型,它提供了一组规则,使您可以推断是否正确使用了内存。对于更高级别的东西,您可以根据等提供的保证进行推理AtomicInteger

使线程安全的标准方法是什么?

在这种情况下,您可以使用AtomicInteger实例,也可以使用原始对象锁定(即synchronized关键字)或Lock对象进行同步。