[英]Use a lock Object to synchronize on my member variable
我正在研究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對象本身? 這不是我看到的唯一使用此技術的示例。 這是一個好習慣嗎?
但是我覺得如果我有兩個用於生產和消費的單獨的類,並且將鏈接列表作為其構造函數的成員,那么我必須在此鏈接列表對象上進行同步,對嗎?
我知道,有班concurrent
包是線程安全的,但是這不是我的問題,我問兩個以上幾種方式之間的最佳做法?
您可以,因為它是您班級的私人成員,只有您可以鎖定。 如果管理得當,則用戶無法僅通過使用類的實例而導致死鎖。 如果該字段是公共的,則用戶可以對此進行鎖定,如果您在類內部使用相同的字段進行同步,則可能會出現死鎖。 這就是為什么我們也不在this指針上加鎖。
但是,使用單個對象進行鎖定有幾個原因:
您可以將對象命名為controlLockerObject(用於序列化公共訪問),listLockerObject(用於序列化對列表的訪問),updateLockerObject(用於序列化用於更新某些內容的代碼區域的訪問)等。
您可以將對象聲明為final,這樣就不會意外替換或刪除用於同步的對象。
為什么我們不應該鎖定LinkedList對象本身?
您甚至可以鎖定LinkedList對象並獲得相同的同步優勢。
但是我覺得如果我有兩個用於生產和消費的單獨的類,並且將鏈接列表作為其構造函數的成員,那么我必須在此鏈接列表對象上進行同步,對嗎?
我不會說您必須在鏈表上進行同步,因為這不是唯一的選擇。 我們可以同時具有生產者線程和使用者線程,以便在任何其他singleton
對象或相同/不同類的某些其他static
字段上進行synchronize(Object.class)/synchronize(String.class) etc
或者像上synchronize(Object.class)/synchronize(String.class) etc
這樣簡單。
我在問以上兩種方式之間的最佳實踐嗎?
關於使用private lock
(正在使用的一種)的優缺點,您可能需要閱讀Concurrency In Practice的Java監視器模式部分 ,它是Java並發性的最佳資源。
具有明確的鎖定對象(使其成為最終對象)很好,並且在關注點分離方面也很好。 它也可以作為文檔,因為可以立即看到“哦,他們在這里做了一些鎖定,也許我也應該這樣做”,這可以防止將來該代碼的開發人員意外地繞過同步。
public class ProduceConsumerTest {
private final int poolsize = 10;
ArrayBlockingQueue<Integer> arrayBlockingQueue = new ArrayBlockingQueue<Integer>(poolsize);
public static void main(String[] args) {
// TODO Auto-generated method stub
ProduceConsumerTest produceConsumerTest = new ProduceConsumerTest();
Producer producer = produceConsumerTest.new Producer();
Consumer consumer = produceConsumerTest.new Consumer();
producer.start();
consumer.start();
}
class Consumer extends Thread{
@Override
public void run(){
Consume();
}
public void Consume(){
while(true){
synchronized (arrayBlockingQueue) {
while(arrayBlockingQueue.size() == 0){
try {
arrayBlockingQueue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
arrayBlockingQueue.notify();
}
}
arrayBlockingQueue.poll();
arrayBlockingQueue.notify();
System.out.println("Consuming:Current size:"+arrayBlockingQueue.size());
}
}
}
}
class Producer extends Thread{
@Override
public void run(){
produce();
}
private void produce(){
while(true){
synchronized (arrayBlockingQueue) {
while(arrayBlockingQueue.size() == poolsize){
try {
arrayBlockingQueue.wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
arrayBlockingQueue.notify();
}
}
arrayBlockingQueue.offer(1);
arrayBlockingQueue.notify();
System.out.println("Producing, Current Size:"+arrayBlockingQueue.size());
}
}
}
}
}
您可以鎖定對象queue
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.