Java 静态变量的线程安全
我有一个如上所述的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() { } } 如果您通过一个实例访问静态数据
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@Nimpublic 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