web-dev-qa-db-ja.com

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()が、アクセスできない他のスレッドによって呼び出されると仮定しましょう。

Synchonizeメソッドを使用して、文字列msgが一度に1つの関数でのみ使用されるようにします。つまり、関数newmsg()getmsg()と同時に実行できません。

65
Winter

それはとても簡単です:

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

I did n'tメソッド自体を同期させるか、thisで同期させることに注意してください。 意図的にロックを公開していない限り、コードのみがアクセスできるオブジェクトのロックのみを取得することをお勧めします。これにより、コードなどとは異なる順序でロックを取得するものが他にないことを安心させることがはるかに容易になります。

152
Jon Skeet

この機能を使用するには、ロックをまったく使用しないほうが良いでしょう。 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);
    }
}

ロックは不要で、コードはシンプルです。いずれにせよ、それはあなたが望むことをする標準的な構造を使用します。

49
Peter Lawrey

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

この簡単な例では、両方のメソッドシグネチャでsynchronizedの後に修飾子としてpublicを置くことができます。

より複雑なシナリオには、他のものが必要です。

3
gd1

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()にある場合、getmsg()を呼び出そうとしても、他のスレッドはsampleのインスタンスをロックできません。

一方、メソッドが長時間実行される場合、synchronizedメソッドの使用がボトルネックになる可能性があります。すべてのスレッドは、インターリーブ可能なオブジェクト内の他のメソッドを呼び出したい場合でも、待機する必要があります。

IMOの簡単な例では、実際にはインターリーブされるべきではない2つのメソッドがあるため、同期メソッドを使用しても問題ありません。ただし、異なる状況では、Joh Skeetの回答に示されているように、同期するロックオブジェクトがある方が理にかなっている場合があります。

2

別の機会に文字列ではなくコレクションを同期している場合、おそらくコレクションを繰り返し処理していて、それが変化するのではないかと心配している場合、Java 5は以下を提供します。

0
SK9