What is StampedLock in Java?

前端 未结 4 1652
清酒与你
清酒与你 2020-12-24 12:50

I am working on a Java code, I need to implement threading in it. I was going through JAVA 8 API and I come to know about Stamped Locks. Can anyone tell me why to use Stampe

相关标签:
4条回答
  • 2020-12-24 13:18

    The API documentation for java.util.concurrent.locks.StampedLock says:

    StampedLocks are designed for use as internal utilities in the development of thread-safe components. Their use relies on knowledge of the internal properties of the data, objects, and methods they are protecting. They are not reentrant, so locked bodies should not call other unknown methods that may try to re-acquire locks (although you may pass a stamp to other methods that can use or convert it). The use of read lock modes relies on the associated code sections being side-effect-free. Unvalidated optimistic read sections cannot call methods that are not known to tolerate potential inconsistencies. Stamps use finite representations, and are not cryptographically secure (i.e., a valid stamp may be guessable). Stamp values may recycle after (no sooner than) one year of continuous operation. A stamp held without use or validation for longer than this period may fail to validate correctly. StampedLocks are serializable, but always deserialize into initial unlocked state, so they are not useful for remote locking.

    e.g. -

     class Point {
       private double x, y;
       private final StampedLock sl = new StampedLock();
    
       void move(double deltaX, double deltaY) { // an exclusively locked method
         long stamp = sl.writeLock();
         try {
           x += deltaX;
           y += deltaY;
         } finally {
           sl.unlockWrite(stamp);
         }
       }
    
       double distanceFromOrigin() { // A read-only method
         long stamp = sl.tryOptimisticRead();
         double currentX = x, currentY = y;
         if (!sl.validate(stamp)) {
            stamp = sl.readLock();
            try {
              currentX = x;
              currentY = y;
            } finally {
               sl.unlockRead(stamp);
            }
         }
         return Math.sqrt(currentX * currentX + currentY * currentY);
       }
    
       void moveIfAtOrigin(double newX, double newY) { // upgrade
         // Could instead start with optimistic, not read mode
         long stamp = sl.readLock();
         try {
           while (x == 0.0 && y == 0.0) {
             long ws = sl.tryConvertToWriteLock(stamp);
             if (ws != 0L) {
               stamp = ws;
               x = newX;
               y = newY;
               break;
             }
             else {
               sl.unlockRead(stamp);
               stamp = sl.writeLock();
             }
           }
         } finally {
           sl.unlock(stamp);
         }
       }
     }
    
    0 讨论(0)
  • 2020-12-24 13:22

    StampedLock support read and write locks. In contrast to ReadWriteLock the locking methods of a StampedLock return a stamp represented by a long value. You can use these stamps to either release a lock or to check if the lock is still valid. Additionally stamped locks support another lock mode called optimistic locking.

    ExecutorService executor = Executors.newFixedThreadPool(2);
            Map<String, String> map = new HashMap<>();
            StampedLock lock = new StampedLock();
    
            executor.submit(() -> {
                long stamp = lock.writeLock();
                try {
                    Thread.sleep(100);
                    map.put("test", "INDIA");
                } catch (Exception e) {
                } finally {
                    lock.unlockWrite(stamp);
                }
            });
    
            Runnable readTask = () -> {
                long stamp = lock.readLock();
                try {
                    System.out.println(map.get("test"));
                    Thread.sleep(100);
                } catch (Exception e) {
                } finally {
                    lock.unlockRead(stamp);
                }
            };
    
            executor.submit(readTask);
            executor.submit(readTask);
    

    Obtaining a read or write lock via readLock() or writeLock() returns a stamp which is later used for unlocking within the finally block. Keep in mind that stamped locks don't implement reentrant characteristics. Each call to lock returns a new stamp and blocks if no lock is available even if the same thread already holds a lock. So you have to pay particular attention not to run into deadlocks.

    executor.submit(() -> {
                long stamp = lock.tryOptimisticRead();
                try {
                    System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
                    Thread.sleep(100);
                    System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
                    Thread.sleep(1000);
                    System.out.println("Optimistic Lock Valid: " + lock.validate(stamp));
                } catch (Exception e) {
                } finally {
                    lock.unlock(stamp);
                }
            });
    
            executor.submit(() -> {
                long stamp = lock.writeLock();
                try {
                    System.out.println("Write Lock acquired");
                    Thread.sleep(100);
                } catch (Exception e) {
                } finally {
                    lock.unlock(stamp);
                    System.out.println("Write done");
                }
            });
    

    An optimistic read lock is acquired by calling tryOptimisticRead() which always returns a stamp without blocking the current thread, no matter if the lock is actually available. If there's already a write lock active the returned stamp equals zero. You can always check if a stamp is valid by calling lock.validate(stamp).

    0 讨论(0)
  • 2020-12-24 13:26

    In addition to @assylias answer:

    StampedLock supports upgrading readLock to writeLock using method tryConvertToWriteLock(long):

    java doc:

    This class also supports methods that conditionally provide conversions across the three modes. For example, method tryConvertToWriteLock(long) attempts to "upgrade" a mode, returning a valid write stamp if (1) already in writing mode (2) in reading mode and there are no other readers or (3) in optimistic mode and the lock is available. The forms of these methods are designed to help reduce some of the code bloat that otherwise occurs in retry-based designs.

    code samples:

    class Point {
       private double x, y;
       private final StampedLock sl = new StampedLock();
    
       // an exclusively locked method
       void move(double deltaX, double deltaY) {
         long stamp = sl.writeLock();
         try {
           x += deltaX;
           y += deltaY;
         } finally {
           sl.unlockWrite(stamp);
         }
       }
    
       // a read-only method
       // upgrade from optimistic read to read lock
       double distanceFromOrigin() {
         long stamp = sl.tryOptimisticRead();
         try {
           retryHoldingLock: for (;; stamp = sl.readLock()) {
             if (stamp == 0L)
               continue retryHoldingLock;
             // possibly racy reads
             double currentX = x;
             double currentY = y;
             if (!sl.validate(stamp))
               continue retryHoldingLock;
             return Math.hypot(currentX, currentY);
           }
         } finally {
           if (StampedLock.isReadLockStamp(stamp))
             sl.unlockRead(stamp);
         }
       }
    
       // upgrade from optimistic read to write lock
       void moveIfAtOrigin(double newX, double newY) {
         long stamp = sl.tryOptimisticRead();
         try {
           retryHoldingLock: for (;; stamp = sl.writeLock()) {
             if (stamp == 0L)
               continue retryHoldingLock;
             // possibly racy reads
             double currentX = x;
             double currentY = y;
             if (!sl.validate(stamp))
               continue retryHoldingLock;
             if (currentX != 0.0 || currentY != 0.0)
               break;
             stamp = sl.tryConvertToWriteLock(stamp);
             if (stamp == 0L)
               continue retryHoldingLock;
             // exclusive access
             x = newX;
             y = newY;
             return;
           }
         } finally {
           if (StampedLock.isWriteLockStamp(stamp))
             sl.unlockWrite(stamp);
         }
       }
    
       // Upgrade read lock to write lock
       void moveIfAtOrigin(double newX, double newY) {
         long stamp = sl.readLock();
         try {
           while (x == 0.0 && y == 0.0) {
             long ws = sl.tryConvertToWriteLock(stamp);
             if (ws != 0L) {
               stamp = ws;
               x = newX;
               y = newY;
               break;
             }
             else {
               sl.unlockRead(stamp);
               stamp = sl.writeLock();
             }
           }
         } finally {
           sl.unlock(stamp);
         }
       }
     }
    
    0 讨论(0)
  • 2020-12-24 13:29

    StampedLock is an alternative to using a ReadWriteLock (implemented by ReentrantReadWriteLock). The main differences between StampedLock and ReentrantReadWriteLock are that:

    • StampedLocks allow optimistic locking for read operations
    • ReentrantLocks are reentrant (StampedLocks are not)

    So if you have a scenario where you have contention (otherwise you may as well use synchronized or a simple Lock) and more readers than writers, using a StampedLock can significantly improve performance.

    However you should measure the performance based on your specific use case before jumping to conclusions.

    Heinz Kabutz has written about StampedLocks in his newsletter and he also made a presentation about performance.

    0 讨论(0)
提交回复
热议问题