使用volatile变量和信号量 - Java

3

我正在学习线程、信号量、volatile变量等IT技术相关知识。

我想知道,在使用信号量时是否需要将变量定义为volatile,我的意思是:

假设有两个线程,一个增加变量的值,另一个减少变量的值。在每次访问变量之前,我会使用互斥锁来控制只有一个线程能够“操作”该变量。

这种情况下,是否需要将变量定义为volatile?


2
为什么不使用AtomicLong来进行这样的操作呢?在同一个整数上正确且高效地实现并发操作并不容易。JDK中有一些现成的类可以帮助你完成这项工作。 - Tagir Valeev
为什么你在这里使用信号量? - Sandeep Kaul
3个回答

5

根据Semaphore的API文档:

内存一致性影响:在线程调用“release”方法(如release())之前发生的操作先于在另一个线程中成功调用“acquire”方法(如acquire())之后发生的操作。

因此,通过信号量保护的变量可以安全地读写。无需将它们声明为volatile


2
信号量不应该用在 synchronized 的位置,因为信号量即使初始化为1,也不会持有独占的互斥锁,就像某个对象上的 synchronized。确实,初始化为1的信号量允许一次仅有一个线程访问持有许可的对象。但是,持有许可的线程并没有拥有它,任何其他线程都可以释放该许可并获取许可。因此,两个线程可以同时访问同一个对象,如果这两个线程都操纵该对象,则会出现多线程问题,如丢失更新、过时读取等。
在您有两个线程的示例中,一个增加变量,另一个减少相同的变量。互斥是足够的,不需要使用 volatile 声明。在这里我假设互斥是通过 synchronized 实现的,而不是通过信号量实现的。
volatile 比 synchronized 更不严格,当执行的操作是原子的(读或写)时,您可能希望使用 volatile。在执行读-更新-写操作时不应使用 volatile。

为什么你假设互斥通过synchronized实现,而他并没有使用synchronized,只用了信号量? - Shondeslitch
因为在这个例子中,他提到了互斥锁(mutex),但从来没有提到过同步(synchronized),而问题是关于信号量(semaphores)和volatile的。由于没有明确提到,我希望他指的是同步(synchronized),因为在这种情况下信号量并不适用。 - Thilak

1
我想知道在使用Semaphore时是否需要将变量定义为volatile。我认为没有这样的限制。互斥锁是一种互斥信号量,它是一种特殊的信号量变体,只允许一个锁定器同时进行。它相当于计数为1的普通计数信号量,并且要求只能由锁定它的同一线程释放。如果我们具体谈论Java中的Semaphore:Semaphore是许可证的计数器,acquire类似于减少等待而不是低于零。它没有上限。如CIP所述:实现没有实际的许可对象,Semaphore不将分配的许可与线程关联,因此在一个线程中获取的许可可以从另一个线程中释放。您可以将acquire视为消耗许可,将release视为创建许可; Semaphore不限于其创建的许可数量。对于您的情况,您可以共享一个计数器并使其成为易失性,或者更好地使用AtomicInteger,因为它们使用CAS机制,在低争用下表现非常好。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接