Java 静态变量的线程安全

Java 静态变量的线程安全,java,multithreading,thread-safety,Java,Multithreading,Thread Safety,我有一个如上所述的Java类。我有这个类的多个线程。在run()方法中,变量a和b每次递增几次。在每次增量中,我都将这些变量放入哈希表中 因此,每个线程将增加这两个变量,并将它们放入哈希表中。如何使这些操作线程安全?使用同步方法,例如 class ABC implements Runnable { private static int a; private static int b; public void run() { } } 如果您通过一个实例访问静态数据

我有一个如上所述的Java类。我有这个类的多个线程。在
run()
方法中,变量
a
b
每次递增几次。在每次增量中,我都将这些变量放入哈希表中


因此,每个线程将增加这两个变量,并将它们放入哈希表中。如何使这些操作线程安全?

使用
同步方法,例如

class ABC implements Runnable {
    private static int a;
    private static int b;
    public void run() {
    }
}
如果您通过一个实例访问静态数据,上述方法很好,但是如果您有多个实例,那么您需要在某个静态对象上进行同步,比如(未测试)

在方法中

private static Object lock = new Object();

注意:这些方法假设您希望在一个原子操作中增加
a
b
,其他答案强调了如何使用原子单独增加它们。

取决于线程安全需要什么。对于这些
int
原语,您需要将它们替换为
AtomicInteger
,或者只在
synchronized
方法或块中使用它们进行操作。如果您需要使跨线程哈希表线程安全,则无需执行任何操作,因为它已同步。

我将使用它,它设计为线程安全,非常易于使用,并为应用程序提供了绝对最小的同步开销:

public void increment()
{
  synchronize(lock)
  {
    a++;b++;
    // do stuff
  }
}

我想补充一些关于这个问题答案的细节

首先,OP询问的是:

如何使这些操作线程安全

在回答这个问题之前,我们需要就什么是线程安全达成一致。我最喜欢的定义是“实践中的Java并发性”,它是

如果类在访问时行为正确,则该类是线程安全的 来自多个线程,而不考虑调度或 运行时交叉执行这些线程 环境,无需额外的同步或其他 调用代码部分的协调

如果你同意这个定义,这意味着我们在进一步讨论之前达到了一致性。让我们回到您所说的操作,它是
a++
b++
,在增量之后,您将把它们放入
哈希表中

对于
a++
操作,它实际上不是一个单独的操作。它遵循读修改写的模型。如您所见,它实际上包含三个单独的步骤。读取值,向其中添加一个值并将其保存回原处。如果有两个线程,在修改和回存操作后,同时读取值为
1
的变量
a
。该值将是
2
,但实际上应该是
3
。为了避免这种情况发生,就像其他人建议的那样,您可以直接使用
AtomicInteger
而不是
int
类型。
AtomicInteger
将保证像increment这样的一些操作以原子方式执行。这意味着,读-修改-写操作不能分割,将作为一个单独的步骤执行。
之后,OP希望将值保存到哈希表中。哈希表是线程安全的容器,不需要其他同步。

希望这个澄清能在其他方面帮助别人。谢谢。

是否不需要在类级别而不是对象级别进行同步?@mcfinnigan-正确,我假设访问是通过单个实例进行的……如果需要,您还可以直接对类对象进行同步:
synchronize(ABC.class){…}
@mcfinnigan-我已经有一段时间没有接触java了,所以有点生锈了!:)哈哈,不用担心。我每天都用它,我总是忘记基本的东西!使int变为volatile并不会使增量操作成为原子操作。它确实允许使用AtomicIntegerFieldDupDate感谢您,似乎我自己对
volatile
有错误的理解。更正了答案。换句话说,静态字段不是线程安全的。只有最后的字段是线程安全的。对于非final的实例字段和静态字段,您必须在同步方法/语句中访问它们,或者使用Java 7/8中提供的并发锁功能来访问它们。不,这不是完全正确的想法。静态或最终与线程安全无关。当然,
final int
将是线程安全的,因为它是不可变的——简单地将可变对象(如
ArrayList
)设置为
final
并不能神奇地使其成为线程安全的对象。使其getter同步也不会有帮助。您必须代理和同步所有实例,而不提供对实例本身的访问。@JavaSE7程序员考试804书中冻结的Spider@Bohemian@Nim
public void run(){synchronized(SharedCounter.class){SharedCounter.count++;}
但是,此代码效率低下,因为它每次获取并释放锁只是为了增加count的值。@sharef注释不用于提问。相反,@Bohemian这是我的问题
public void increment()
{
  synchronize(lock)
  {
    a++;b++;
    // do stuff
  }
}
class ABC implements Runnable {
    private static AtomicInteger a;
    private static AtomicInteger b;
    public void run() {
        // effectively a++, but no need for explicit synchronization!
        a.incrementAndGet(); 
    }
}

// In some other thread:

int i = ABC.a.intValue(); // thread-safe without explicit synchronization