ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • ReentrantReadWriteLock
    StaticPL/JAVA 2020. 2. 27. 11:48

    1. Overview

    2. ReentrantReadWriteLock

    2.1 Why

    Multiple threads can safely read from a shared resource concurrently as long as they are not modifying its state.

    2.2 Usage

    • Synchronized and ReentrantLock do not allow multiple readers to access a shared resource concurrently.
    • Not a big problem in the general case
    • If we keep the critical sections short, the chances of contention over a lock are minimal

    2.3 When to use

    • When read operations are predominant
    • Or when the read operations are not as fast
      • Read from many variables
      • Read from a complex data structure
    • Mutual exclusion of reading threads negatively impacts the performance

    2.4 How to use

    ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();
    Lock readLock = rwLock.readLock();
    Lock writeLock = rwLock.writeLock();
    
    writeLock.lock();
    try {
    	modifySharedResource();
    } finally {
    	writeLock.unlock();
    }
    
    readLock.lock();
    try {
    	readFromSharedResource();
    } finally{
    	readLock.unlock();
    }

    2.4 Mutual exclusion between readers and writers

    3. Example

    public class rerwlDemo {
        public static final int HIGHEST_PRICE = 1000;
        public static void main(String[] args) throws InterruptedException {
            InventoryDatabase inventoryDatabase = new InventoryDatabase();
            Random random = new Random();
            for (int i = 0; i < 100000; i++) {
                inventoryDatabase.addItem(random.nextInt(HIGHEST_PRICE));
            }
            Thread writer = new Thread(() -> {
                while (true) {
                    inventoryDatabase.addItem(random.nextInt(HIGHEST_PRICE));
                    inventoryDatabase.removeItem(random.nextInt(HIGHEST_PRICE));
    
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            writer.setDaemon(true);
            writer.start();
    
            int numberOfReaderThreads = 7;
            List<Thread> readers = new ArrayList<>();
            for (int i = 0; i < numberOfReaderThreads; i++) {
                Thread reader = new Thread(() -> {
                    for (int j = 0; j < 100000; j++) {
                        int upperBoundPrice = random.nextInt(HIGHEST_PRICE);
                        int lowerBoundPrice = upperBoundPrice > 0 ? random.nextInt(upperBoundPrice) : 0;
                        inventoryDatabase.getNumberOfItemsInPriceRange(lowerBoundPrice, upperBoundPrice);
                    }
                });
                reader.setDaemon(true);
                readers.add(reader);
            }
            long startReadingTime = System.currentTimeMillis();
            for (Thread reader: readers) {
                reader.start();
            }
            for (Thread reader: readers) {
                reader.join();
            }
            long endReadingTime = System.currentTimeMillis();
            System.out.println(String.format("Reading took %d ms", endReadingTime - startReadingTime));
        }
        public static class InventoryDatabase {
            // the tree map which is implemented by a type of
            // binary search tree called red-black tree
            private TreeMap<Integer, Integer> priceToCountMap = new TreeMap<>();
            private ReentrantLock lock = new ReentrantLock();
            private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
            private Lock readLock = reentrantReadWriteLock.readLock();
            private Lock writeLock = reentrantReadWriteLock.writeLock();
            public int getNumberOfItemsInPriceRange(int lowerBound, int upperBound) {
    //            lock.lock();
                readLock.lock();
                try {
                    Integer fromKey = priceToCountMap.ceilingKey(lowerBound);
                    Integer toKey = priceToCountMap.floorKey(upperBound);
                    if(fromKey == null || toKey == null) {
                        return 0;
                    }
                    NavigableMap<Integer, Integer> rangeOfPrices = priceToCountMap.subMap(fromKey, true, toKey, true);
                    int sum = 0;
                    for (int numberOfItemsForPrice: rangeOfPrices.values()) {
                        sum += numberOfItemsForPrice;
                    }
                    return sum;
                } finally {
    //                lock.unlock();
                    readLock.unlock();
                }
            }
            public void addItem(int price) {
    //            lock.lock();
                writeLock.lock();
                try {
                    Integer numberOfItemsForPrice = priceToCountMap.get(price);
                    if (numberOfItemsForPrice == null) {
                        priceToCountMap.put(price, 1);
                    } else {
                        priceToCountMap.put(price, numberOfItemsForPrice + 1);
                    }
                } finally {
    //                lock.unlock();
                    writeLock.unlock();
                }
            }
            public void removeItem(int price) {
    //            lock.lock();
                writeLock.lock();
                try {
                    Integer numberOfItemsForPrice = priceToCountMap.get(price);
                    if (numberOfItemsForPrice == null || numberOfItemsForPrice == 1) {
                        priceToCountMap.remove(price);
                    } else {
                        priceToCountMap.put(price, numberOfItemsForPrice - 1);
                    }
                } finally {
    //                lock.unlock();
                    writeLock.unlock();
                }
            }
        }
    }

    4. Reference

    https://www.geeksforgeeks.org/treemap-ceilingkey-in-java-with-examples/

    'StaticPL > JAVA' 카테고리의 다른 글

    Non-blocking, Lock-free operations  (0) 2020.02.27
    Semaphore  (0) 2020.02.27
    ReentrantLock, lockInterruptibly, and tryLock  (0) 2020.02.27
    synchronized  (0) 2020.02.27
    Atomic Variables  (0) 2020.02.26

    댓글

Designed by Tistory.