如何在 Java 中同步或锁定变量?

让我用这个小而简单的例子:

class Sample {
private String msg = null;


public void newmsg(String x){
msg = x;
}


public String getmsg(){
String temp = msg;
msg = null;
return temp;
}
}

让我们假设函数 newmsg()是由其他线程调用的,我没有访问这些线程的权限。

我想使用 sync 方法来保证字符串 msg每次只被一个函数使用。换句话说,函数 newmsg()不能与 getmsg()同时运行。

158898 次浏览

在这个简单的示例中,您可以在两个方法签名中都将 synchronized作为 public之后的修饰符。

更复杂的场景需要其他东西。

这很简单:

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上同步。我坚信这是一个好主意,只获取对象的锁,只有你的代码可以访问,除非你的 故意的暴露锁。它让你更容易确信没有其他东西会以与你的代码不同的顺序获得锁,等等。

使用 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,在您的简单示例中,使用同步方法是可以的,因为您实际上有两个不应该交错的方法。但是,在不同的情况下,使用 lock 对象进行同步可能更有意义,如 Joh Skeet 的答案所示。

对于这个功能,最好根本不要使用锁。

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 1.5开始,考虑 Java.util.concurlpackage 总是一个好主意。它们是目前 Java 中最先进的锁定机制。同步机制比 java.util.while 类更重量级。

这个例子大概是这样的:

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();
}
}
}

如果在另一种情况下,您正在同步一个集合而不是一个字符串,也许您正在对集合进行迭代,并且担心它会发生变化,Java5提供: