如何在 Java 中同步或锁定变量?
Posted
技术标签:
【中文标题】如何在 Java 中同步或锁定变量?【英文标题】:How to synchronize or lock upon variables in Java? 【发布时间】:2011-08-17 05:47:47 【问题描述】:让我用这个小而简单的例子:
class Sample
private String msg = null;
public void newmsg(String x)
msg = x;
public String getmsg()
String temp = msg;
msg = null;
return temp;
假设函数newmsg()
被我无权访问的其他线程调用。
我想使用synchonize方法来保证字符串msg
每次只被一个函数使用。也就是说,函数newmsg()
不能和getmsg()
同时运行。
【问题讨论】:
你在问如何在Java中使用“同步”关键字吗?一个简单的谷歌搜索返回了很多有用的点击,包括这个download.oracle.com/javase/tutorial/essential/concurrency/… 顺便说一句,调用 getmsg() 方法会更好,比如 popmsg() 或 consumemsg(),因为它会修改类 【参考方案1】:这很简单:
class Sample
private String message = null;
private final Object lock = new Object();
public void newMessage(String x)
synchronized (lock)
message = x;
public String getMessage()
synchronized (lock)
String temp = message;
message = null;
return temp;
请注意,我没有使方法本身同步或在this
上同步。我坚信只获取只有您的代码可以访问的对象的锁是个好主意,除非您故意公开锁。它使您更容易向自己保证,没有其他东西会以与您的代码不同的顺序获取锁,等等。
【讨论】:
设置同步功能和创建这个锁有什么区别? @Giacomo:我喜欢我的代码是可读的,并且设计得让我可以推理它。当涉及多线程时,这一点非常重要。毫不夸张地说,这是我几乎总是使用的那种代码。至于是否浪费...您认为这种浪费多久会很严重? @Giacomo:关键是很明显,您正在锁定其他人无法锁定的东西。这似乎相当直截了当:你阅读了代码,你就完成了。现在考虑您的示例:您如何知道可能在您的对象上同步?您如何理解锁定行为?您必须阅读所有所涉及的代码。因此,如果您实际上对正确的行为感到困扰,我会说它的可读性较差。坦率地说,我认为允许 all 对象在 Java 中被锁定是错误的。 (不幸的是,.NET 复制了一个错误。) @Winter 同步方法阻止“this”,因此 Sample 对象本身。但是对象“锁定”上的同步块将阻止“锁定”,而不是样本 @PhilipRego:首先,因为那样被锁定的值会发生变化,这会使事情在各种方面变得不安全。其次,因为这很容易意味着多个对象之间无意共享锁,从而导致各种其他问题。第三:关注点分离。我非常热衷于有一个目的的领域。【参考方案2】:对于这个功能,你最好不要使用锁。试试 AtomicReference。
public class Sample
private final AtomicReference<String> msg = new AtomicReference<String>();
public void setMsg(String x)
msg.set(x);
public String getMsg()
return msg.getAndSet(null);
不需要锁,代码更简单恕我直言。在任何情况下,它都使用标准构造来满足您的需求。
【讨论】:
仅供参考,java.util.concurrent.atomic
中有用于基本类型的类,例如 AtomicBoolean
和 AtomicInteger
【参考方案3】:
从 Java 1.5 开始,考虑 java.util.concurrent 包总是一个好主意。它们是目前 Java 中最先进的锁定机制。同步机制比 java.util.concurrent 类更重量级。
示例如下所示:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class Sample
private final Lock lock = new ReentrantLock();
private String message = null;
public void newmsg(String msg)
lock.lock();
try
message = msg;
finally
lock.unlock();
public String getmsg()
lock.lock();
try
String temp = message;
message = null;
return temp;
finally
lock.unlock();
【讨论】:
根据docs,锁定应该在try块之前完成。【参考方案4】:使用synchronized
关键字。
class sample
private String msg=null;
public synchronized void newmsg(String x)
msg=x;
public synchronized string getmsg()
String temp=msg;
msg=null;
return msg;
在方法上使用synchronized
关键字将要求线程获得sample
实例上的锁。因此,如果任何一个线程在newmsg()
中,则没有其他线程能够获得sample
实例的锁定,即使它试图调用getmsg()
。
另一方面,如果您的方法执行长时间运行的操作,使用synchronized
方法可能会成为瓶颈 - 所有线程,即使它们想要调用该对象中可能交错的其他方法,仍然必须等待.
IMO,在您的简单示例中,可以使用同步方法,因为您实际上有两个不应该交错的方法。但是,在不同的情况下,使用锁对象进行同步可能更有意义,如 Joh Skeet 的回答所示。
【讨论】:
【参考方案5】:在这个简单的示例中,您可以在两个方法签名中将 synchronized
作为修饰符放在 public
之后。
更复杂的场景需要其他东西。
【讨论】:
【参考方案6】:如果在其他情况下您正在同步一个集合而不是一个字符串,那么您可能正在对集合进行迭代并且担心它会发生变异,Java 5 提供了:
CopyOnWriteArrayList
CopyOnWriteArraySet
【讨论】:
以上是关于如何在 Java 中同步或锁定变量?的主要内容,如果未能解决你的问题,请参考以下文章