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


77

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

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


4
您是否在问如何在Java中使用“同步”关键字?一个简单的谷歌搜索回来了很多有用的结果,包括这一个download.oracle.com/javase/tutorial/essential/concurrency/...
卡尔

4
顺便说一句,最好调用我们的getmsg()方法,例如popmsg()或Consumermsg(),因为它会修改类
Naumcho 2011年

Answers:


170

那很简单:

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。我坚信,除非您有意公开该锁,否则仅对只有您的代码才能访问的对象获取锁是个好主意。这样可以很容易地向自己保证,其他任何东西都不会以与您的代码不同的顺序获取锁,等等。


8
设置同步功能或创建此锁有什么区别?

3
@Giacomo:我希望我的代码可读性强,并经过精心设计,以便能够对此进行推理。当涉及多线程时,这非常重要。毫不夸张地说,这是我几乎一直使用的代码。至于浪费……您会期望这种浪费有多大?
乔恩·斯基特

3
@Giacomo:重点是,很明显,您锁定的是其他人无法做到的。这似乎很容易理解:您阅读了代码,然后就完成了。现在考虑您的示例:如何知道对象上可能进行同步?您如何了解锁定行为?您必须阅读所有涉及的代码。因此,我想说的是,如果您实际上被正确的行为所困扰,那么它的可读性就会降低。坦白地说,我认为允许所有对象都用Java锁定是错误的。(很遗憾,.NET复制了一个错误。)
乔恩·斯凯特

3
@Winter同步方法会阻止“ this”,因此Sample对象本身也是如此。但是对象“锁定”上的同步块将阻止“锁定”,而不是样本
Jemshit Iskenderov

2
@PhilipRego:首先,因为然后锁定的值将发生变化,这使事情在各种方面都不安全。其次,因为它很容易意味着锁在多个对象之间无意间共享,从而导致各种其他问题。第三:关注点分离。我非常热衷于一个目的明确的领域。
乔恩·斯基特

53

对于此功能,最好不要使用锁。尝试一个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);
    }
}

不需要锁,代码更简单,恕我直言。无论如何,它都使用标准构造来满足您的需求。


9
仅供参考,有一些java.util.concurrent.atomic原始类型的类,例如AtomicBooleanandAtomicInteger
Hugh Jeffner

10

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

1
根据文档,锁定应在try块之前完成。
亚伦·罗勒

3

在这个简单的示例中,您可以在两个方法签名中都放synchronized一个修饰符public

更复杂的场景需要其他内容。


3

使用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的回答所示。


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.