私はマルチスレッドのトピックであるJavaのより高度なトピックを勉強しています。ロックオブジェクトを使用してメンバー変数を同期する
多くのコードでは、別のオブジェクトロックObject lock = new Object();
を使用して、一部のクラスデータメンバーを同期させています。
package multithreading;
import java.util.LinkedList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
class ProduceConsume {
private LinkedList<Integer> queue = new LinkedList<>();
private final int LIMIT = 10;
private final Object lock = new Object();
public void produce() throws InterruptedException {
int value = 0;
while (true) {
synchronized (lock) {
while (queue.size() == LIMIT) {
lock.wait();
}
queue.add(value++);
lock.notify();
}
}
}
public void consume() throws InterruptedException {
while (true) {
Thread.sleep(1000);
synchronized (lock) {
while (queue.size() == 0) {
lock.wait();
}
System.out.print("Size is: " + queue.size());
int value = queue.removeFirst();
System.out.println("; value is: " + value);
lock.notify();
}
}
}
}
public class ProducerConsumerWaitNotify {
public static void main(String[] args) throws InterruptedException {
ProduceConsume object = new ProduceConsume();
ExecutorService execuor = Executors.newFixedThreadPool(2);
execuor.submit(new Runnable() {
@Override
public void run() {
try {
object.produce();
} catch (InterruptedException ex) {
Logger.getLogger(ProducerConsumerWaitNotify.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
execuor.submit(new Runnable() {
@Override
public void run() {
try {
object.consume();
} catch (InterruptedException ex) {
Logger.getLogger(ProducerConsumerWaitNotify.class.getName()).log(Level.SEVERE, null, ex);
}
}
});
execuor.shutdown();
execuor.awaitTermination(1000, TimeUnit.DAYS);
}
}
なぜLinkedListオブジェクト自体をロックするべきではないのですか?これは私がこの手法を使って見た唯一の例ではありません。それは良い練習ですか?
しかし私は、プロダクションと消費の2つのクラスがあり、リンクされたリストをコンストラクタのメンバーとして受け取ると、このリンクされたリストオブジェクトで同期する必要があります。
concurrent
パッケージにはスレッドセーフなクラスがあることは知っていますが、これは私の質問ではありません。上記の2つの方法の間のベストプラクティスについて質問していますか?
オブジェクトをロックするには、final修飾子を使用する必要があります。そうしないと、ある時点で 'lock'参照ポイントを新しいオブジェクトにすることは可能です。だから、異なるモニターでシンクロナイズすることは可能です。それはまったく同期しないのと同じです。 –
以下の答えに埋もれている重要なアイデアは、あなたがあなたのロックオブジェクトに使うものはどれでも、それは 'private'でなければならないということです。クライアントクラスに、ロックのために使用するものと同じオブジェクトに対してロックを行う機能を与えることは決して望ましくありません。理想的な世界では、クライアントプログラマーはそれをしないほどスマートになりますが...とにかく、誰かのプログラムがデッドロックやその他の悪い行為に陥るリスクを軽減します。 –