簡體   English   中英

使用鎖對象在我的成員變量上同步

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM