簡體   English   中英

讀/寫鎖比同步慢,即使只讀取?

[英]Read/write lock is slower than synchronized, even when only reading?

我有以下代碼 ArrayList 實現

public class LongArrayListUnsafe {
    public static void main(String[] args) {
        LongArrayList dal1 = LongArrayList.withElements();
        for (int i = 0; i < 1000; i++)
            dal1.add(i);

        // Runtime.getRuntime().availableProcessors()
        ExecutorService executorService = Executors.newFixedThreadPool(4);


        long start = System.nanoTime();
        for (int i = 0; i < 100; i++) {
            executorService.execute(new Runnable() {
                public void run() {
                    for (int i = 0; i < 1000; i++)
                        dal1.size();
                    for (int i = 0; i < 1000; i++)
                        dal1.get(i % 100);

                }
            });
        }
        executorService.shutdown();

        try {
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            System.out.println("mayor disaster!");
        }
    }

    class LongArrayList {
        private long[] items;
        private int size;

        public LongArrayList() {
            reset();
        }

        public static LongArrayList withElements(long...initialValues) {
            LongArrayList list = new LongArrayList();
            for (long l: initialValues)
                list.add(l);
            return list;
        }

        // Number of items in the double list
        public synchronized int size() {
            return size;
        }

        // Return item number i
        public synchronized long get(int i) {
            if (0 <= i && i < size)
                return items[i];
            else
                throw new IndexOutOfBoundsException(String.valueOf(i));
        }

        // Add item x to end of list
        public synchronized LongArrayList add(long x) {
            if (size == items.length) {
                long[] newItems = new long[items.length * 2];
                for (int i = 0; i < items.length; i++)
                    newItems[i] = items[i];
                items = newItems;
            }
            items[size] = x;
            size++;
            return this;
        }

現在,這個並發驅動程序代碼只是讀取已經生成的列表。這進行得非常快。 但是我想知道我是否可以使用讀寫鎖更快地執行此只讀操作。 在大小和獲取方面,這看起來像這樣:

synchronized public int size() {
    readWriteLock.readLock().lock();
    int ret = this.size.get();
    readWriteLock.readLock().unlock();

    return ret;
}

public long get(int i) {
    readWriteLock.readLock().lock();
    if (0 <= i && i < size.get()) {

        long ret = items.get(i);
        readWriteLock.readLock().unlock();
        return ret;
    } else {
        throw new IndexOutOfBoundsException(String.valueOf(i));
    }
}

但是,使用讀寫鎖會變慢,當我添加更多線程時會更慢。 為什么是這樣? 當我的驅動程序代碼只是讀取時,線程應該或多或少地無限制地訪問方法?

java.util.concurrent.locks.ReadWriteLock本質上比諸如synchronized類的互斥鎖更復雜。 該類的文檔說明了這一點。 讀寫語義的開銷可能大於return this.size; , 或return this.items[i]; ,即使有周圍的邊界檢查。

讓我們也特別看看你的提議。 你想替換原來的

public synchronized int size() {
    return size;
}

與提案

synchronized public int size() {           // <-- locks exclusively/mutually on "this"
    readWriteLock.readLock().lock();       // <-- locks on readWriteLock.readLock()
    int ret = this.size.get();             // <-- is size and AtomicInteger now?
    readWriteLock.readLock().unlock();
    
    return ret;
}

我認為使用synchronized是一個錯字,或者它會給等式添加另一個鎖。 另外,我假設this.size.get(); 應該是this.size; . (在這種情況下使用 AttomicInteger 作為大小沒有意義並增加了額外的成本)。 如果我的假設是正確的,您的實際建議將是:

public int size() {
    readWriteLock.readLock().lock();
    int ret = this.size; 
    readWriteLock.readLock().unlock();

    return ret;
}
public long get(int i) {
    readWriteLock.readLock().lock();
    if (0 <= i && i < this.size) {
        long ret = items[i];
        readWriteLock.readLock().unlock();
        return ret;
    } else {
        throw new IndexOutOfBoundsException(String.valueOf(i));
    }
}
public LongArrayList add(long x) {
    readWriteLock.writeLock().lock();
    if (size == items.length) {
        long[] newItems = new long[items.length * 2];
        for (int i = 0; i < items.length; i++)
            newItems[i] = items[i];
        this.items = newItems;
    }
    items[size] = x;
    size++;
    readWriteLock.writeLock().unlock();
    return this;
}

get(int)是危險的。 如果拋出IndexOutOfBoundException ,則讀鎖將永遠保持鎖定狀態。 這不會減慢進一步讀取的速度,但它會使所有未來的add(long)調用保持等待。 如果使用鎖,建議與finally結合使用,以確保它被解鎖:

public long get(int i) {
    readWriteLock.readLock().lock();
    try {
        if (0 <= i && i < size) {
            return items[i];
        } 
        throw new IndexOutOfBoundsException(String.valueOf(i));
    }
    finally {
        readWriteLock.readLock().unlock();
    }
}
public LongArrayList add(long x) {
    readWriteLock.writeLock().lock();
    try {
        if (size == items.length) {
            long[] newItems = new long[items.length * 2];
            for (int i = 0; i < items.length; i++)
                newItems[i] = items[i];
            items = newItems;
        }
        items[size] = x;
        size++;
    }
    finally {
        readWriteLock.writeLock().unlock();
    }
    return this;
}

如前所述,如果您閱讀的內容遠遠多於您編寫的內容,則使用synchronized可能會更高效。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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