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

时间:2011-05-02 20:02:38

标签: java synchronization

让我使用这个小而简单的样本:

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()同时运行。

6 个答案:

答案 0 :(得分:150)

这很简单:

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上同步。我坚信只对你的代码才能访问的对象获取锁定是个好主意,除非你故意暴露锁。这样可以更容易地向自己保证,其他任何东西都不会以与代码不同的顺序获取锁定等。

答案 1 :(得分:48)

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

无需锁定,代码更简单恕我直言。在任何情况下,它都使用一个符合你想要的标准结构。

答案 2 :(得分:7)

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

答案 3 :(得分:2)

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

更复杂的情况需要其他东西。

答案 4 :(得分:1)

使用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 :(得分:0)

如果在另一个场合你正在同步一个Collection而不是一个String,那么你可能正在迭代该集合并担心它会发生变异,Java 5提供: