Java 同步、易失和(标记)锁的内存模型交互
Java Memory Model interaction of synchronization, volatile and (stamped) locks
使用锁来保证内存可见性时是否需要 volatile
修饰符?
试图完全理解并发性、内存可见性和执行控制我遇到了几个消息来源说在 synchronized
块中更新的变量不需要字段是 volatile
(大多数没有给出来源和实际上有一页说同步方法和波动场需要结合使用。
当接近 jls chapter 17.4.5 我发现:
Two actions can be ordered by a happens-before relationship. If one action
happens-before another, then the first is visible to and ordered before the second.
这部分是说后续的同步方法调用保护同一个变量变量将确保它对第二个线程可见吗?如果是这种情况,对于锁也是如此,因为我们也可以保证顺序吗?
另一方面,当我们突然拥有允许 2 个线程访问该字段的写锁时会发生什么。即使在变量被解锁的情况下,整个构造是否崩溃并且线程永远不会保证更新它们的缓存?
简码
int field; //volatile not needed because we have a definite happens-before relationship
Lock lock;
void update(){
//No matter how many threads access this method they will always have
//the most up to date field value to work with.
lock.lock()
field *= 2;
lock.unlock();
}
来自 Lock
的 API 文档:
https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/Lock.html
All Lock implementations must enforce the same memory synchronization
semantics as provided by the built-in monitor lock, as described in
Chapter 17 of The Java™ Language Specification:
- A successful lock operation has the same memory synchronization effects as a successful Lock action.
- A successful unlock operation has the same memory synchronization effects as a successful Unlock action.
Unsuccessful locking and unlocking operations, and reentrant
locking/unlocking operations, do not require any memory
synchronization effects.
我有点不清楚,但要点是,是的,Lock
需要以与监视器相同的方式工作(synchronized
关键字的作用),因此您的示例确实如此始终使 field
的最新更新可见,而无需明确使用 volatile
关键字。
P.S。阅读 Brian Goetz 的 Java 并发实践, 它更详细地解释了所有这些内容。它基本上是 Java.
中所有事物并发的圣经
...and actually one page saying synchronized methods and volatility fields need to be used in conjunction.
您可以将您需要了解的有关内存可见性和 synchronized
块的所有信息提炼为一个简单的规则。也就是说,线程 A 在退出 synchronized (o) {...}
块之前对共享变量和对象所做的任何事情都保证在线程 B 进入 synchronized (o) {...}
块时对线程 B 可见 for同一个对象, o
.
而且,正如@markspace 已经说过的,java.util.concurrent.locks.Lock
的任何实现都需要以相同的方式工作。
Is the volatile modifier required when working with locks to guarantee memory visibility?
volatile
变量只保证内存可见性,不保证原子性。这是 volatile
和 Java 中的 synchronized
块之间的主要区别之一。所以当你使用 synchronized
块时,变量不必是 volatile
。但是,如果您的变量是 volatile
并对该变量执行任何复合操作,那么您需要使用锁来保护对 volatile
变量的更新。
Is this the section which says that subsequent synchronized method calls guarding the same variable will ensure it to be visible to the second thread? If this is the case does the same hold true for locks since we can also guarantee an order?
是的。因为锁会给你可见性和原子性。
On the other hand what happens when suddenly we have write locks allowing 2 threads to access the field. Does the entire construct collapse and threads are never guaranteed to updated their cache even in the event if the variable is unlocked?
如果您在同一个锁上保护对变量的更新,则在任何给定时间只有一个线程可以处理该变量。所以它保证了一致性。但是,如果您每次都使用不同的锁来保护该变量,那么就会有多个线程修改变量状态,并可能导致变量状态不一致。因此,在这种情况下,可见性和原子性都得到了保证,但仍然会导致不一致。
使用锁来保证内存可见性时是否需要 volatile
修饰符?
试图完全理解并发性、内存可见性和执行控制我遇到了几个消息来源说在 synchronized
块中更新的变量不需要字段是 volatile
(大多数没有给出来源和实际上有一页说同步方法和波动场需要结合使用。
当接近 jls chapter 17.4.5 我发现:
Two actions can be ordered by a happens-before relationship. If one action happens-before another, then the first is visible to and ordered before the second.
这部分是说后续的同步方法调用保护同一个变量变量将确保它对第二个线程可见吗?如果是这种情况,对于锁也是如此,因为我们也可以保证顺序吗?
另一方面,当我们突然拥有允许 2 个线程访问该字段的写锁时会发生什么。即使在变量被解锁的情况下,整个构造是否崩溃并且线程永远不会保证更新它们的缓存?
简码
int field; //volatile not needed because we have a definite happens-before relationship
Lock lock;
void update(){
//No matter how many threads access this method they will always have
//the most up to date field value to work with.
lock.lock()
field *= 2;
lock.unlock();
}
来自 Lock
的 API 文档:
https://docs.oracle.com/javase/10/docs/api/java/util/concurrent/locks/Lock.html
All Lock implementations must enforce the same memory synchronization semantics as provided by the built-in monitor lock, as described in Chapter 17 of The Java™ Language Specification:
- A successful lock operation has the same memory synchronization effects as a successful Lock action.
- A successful unlock operation has the same memory synchronization effects as a successful Unlock action.
Unsuccessful locking and unlocking operations, and reentrant locking/unlocking operations, do not require any memory synchronization effects.
我有点不清楚,但要点是,是的,Lock
需要以与监视器相同的方式工作(synchronized
关键字的作用),因此您的示例确实如此始终使 field
的最新更新可见,而无需明确使用 volatile
关键字。
P.S。阅读 Brian Goetz 的 Java 并发实践, 它更详细地解释了所有这些内容。它基本上是 Java.
中所有事物并发的圣经...and actually one page saying synchronized methods and volatility fields need to be used in conjunction.
您可以将您需要了解的有关内存可见性和 synchronized
块的所有信息提炼为一个简单的规则。也就是说,线程 A 在退出 synchronized (o) {...}
块之前对共享变量和对象所做的任何事情都保证在线程 B 进入 synchronized (o) {...}
块时对线程 B 可见 for同一个对象, o
.
而且,正如@markspace 已经说过的,java.util.concurrent.locks.Lock
的任何实现都需要以相同的方式工作。
Is the volatile modifier required when working with locks to guarantee memory visibility?
volatile
变量只保证内存可见性,不保证原子性。这是 volatile
和 Java 中的 synchronized
块之间的主要区别之一。所以当你使用 synchronized
块时,变量不必是 volatile
。但是,如果您的变量是 volatile
并对该变量执行任何复合操作,那么您需要使用锁来保护对 volatile
变量的更新。
Is this the section which says that subsequent synchronized method calls guarding the same variable will ensure it to be visible to the second thread? If this is the case does the same hold true for locks since we can also guarantee an order?
是的。因为锁会给你可见性和原子性。
On the other hand what happens when suddenly we have write locks allowing 2 threads to access the field. Does the entire construct collapse and threads are never guaranteed to updated their cache even in the event if the variable is unlocked?
如果您在同一个锁上保护对变量的更新,则在任何给定时间只有一个线程可以处理该变量。所以它保证了一致性。但是,如果您每次都使用不同的锁来保护该变量,那么就会有多个线程修改变量状态,并可能导致变量状态不一致。因此,在这种情况下,可见性和原子性都得到了保证,但仍然会导致不一致。