阅读 290

并发编程9-公平锁&ReentrantReadWriteLock&StampedLock

1.公平锁/非公平锁

1.1 公平锁概述

公平是针对锁的获取而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序。
ReentrantLock、ReadWriteLock默认都是非公平模式,非公平锁的效率高于公平锁(不浪费的线程切换).

1.2 自写公平锁

FairLock,QueueObject

import java.util.ArrayList;
import java.util.List;

//该类去掉了可重入的功能.
//https://gitee.com/zgy/codes/7seiqx2nwuo4dhrj8pf6m33这里有公平锁代码
public class FairLock {
	private boolean isLocked = false;
	private Thread lockingThread = null;
	//对列来记录调用lock的顺序
	private List<QueueObject> waitingThreads = new ArrayList<QueueObject>();

	//每来一个线程调用lock就记录产生一个QueueObject,然后保存到队列中.
	//this是调用lock的线程自己.
	public void lock() throws InterruptedException {
		QueueObject queueObject = new QueueObject();
		synchronized (this) {
			waitingThreads.add(queueObject);
		}

		try {
			queueObject.doWait();
		} catch (InterruptedException e) {
			synchronized (this) {
				waitingThreads.remove(queueObject);
			}
			throw e;
		}
	}

	//线程调用unlock的时候,取队列中的第一个donotify
	public synchronized void unlock() {
		if (this.lockingThread != Thread.currentThread()) {
			throw new IllegalMonitorStateException("Calling thread has not locked this lock");
		}
		isLocked = false;
		lockingThread = null;
		if (waitingThreads.size() > 0) {
			waitingThreads.get(0).doNotify();
		}
	}
}


 


public class QueueObject {

	//是否叫醒.
	private boolean isNotified = false;

	public synchronized void doWait() throws InterruptedException {
		//死循环,如果没有叫醒就一直等待
		while (!isNotified) {
			this.wait();
		}
		this.isNotified = false;
	}

	public synchronized void doNotify() {
		this.isNotified = true;
		this.notify();
	}

	public boolean equals(Object o) {
		return this == o;
	}

}

复制代码

1.3 ReentrantLock中的公平锁和非公平锁

ReentrantLock.FairSync ReentrantLock.FairSync

package java.util.concurrent.locks;
public class ReentrantLock implements Lock, java.io.Serializable {
    
    //1.Sync
    abstract static class Sync extends AbstractQueuedSynchronizer {
        final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
    }
    
    
    
    //2.FairSync
    static final class FairSync extends Sync {
        //公平的tryAcquire
         protected final boolean tryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
             //和非公平的区别是有这句话,也就是没有前驱的情况下,也就是自己是第一个的时候才返回true.
             //如果有前置则返回false,等待前置的线程被获取到cpu
                if (!hasQueuedPredecessors() &&
                    compareAndSetState(0, acquires)) {
                    setExclusiveOwnerThread(current);
                    return true;
                }
            }
            else if (current == getExclusiveOwnerThread()) {
                int nextc = c + acquires;
                if (nextc < 0)
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }
        
    }
    
    
    //3. NonfairSync
      static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;
 
        final void lock() {
            if (compareAndSetState(0, 1))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }
     
     
 }
复制代码

1.4 ReentrantReadWriteLock 也有公平锁和非公平锁.

package java.util.concurrent.locks;
public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {

    abstract static class Sync extends AbstractQueuedSynchronizer {
    
    
    }
        
    
    static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }
    
    
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            return false; // writers can always barge
        }
        final boolean readerShouldBlock() { 
            return apparentlyFirstQueuedIsExclusive();
        }
    }
        
        
}        
复制代码

2.排它锁和共享锁

2.1 概述

  • 排它锁

排它锁,也称作独占锁,一个锁在某一时刻只能被一个线程占有,其它线程必须等待锁被释放之后才可能获取到锁。(原子性,排斥其它线程进入. )

  • 共享锁

共享锁就是允许多个线程同时获取一个锁,一个锁可以同时被多个线程拥有。(可以有多个线程同时访问)

2.2 举例

  • ReentrantLock就是一种排它锁。CountDownLatch是一种共享锁。这两类都是单纯的一类,即,要么是排它锁,要么是共享锁。
  • ReentrantReadWriteLock是同时包含排它锁和共享锁特性的一种锁,这里主要以ReentrantReadWriteLock为例来进行分析学习。我们使用ReentrantReadWriteLock的写锁时,使用的便是排它锁的特性;使用ReentrantReadWriteLock的读锁时,使用的便是共享锁的特性。

3.读写锁

3.1 读写锁概述

同时包含排它锁和共享锁,写锁是排它锁,读锁是共享锁.

  • 读读:可以多线程进入,保证读的性能提高,线程不互斥等待
  • 读写:线程互斥等待,只要是写的时候不能进行其它操作,多个读可以同时进行.
  • 写写:线程互斥等待

ReentrantReadWriteLock用的也有公平锁和非公平锁.

3.2 示例


import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class MyReadWriteLock {

    private Map<String, Object> map = new HashMap<>();

    private ReadWriteLock rwl = new ReentrantReadWriteLock();

    private Lock r = rwl.readLock();
    private Lock w = rwl.writeLock();

    public Object get(String key) {
        r.lock();
        System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
        try {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return map.get(key);
        } finally {
            r.unlock();
            System.out.println(Thread.currentThread().getName() + " 读操执行完毕..");
        }
    }

    public void put(String key, Object value) {
        w.lock();
        System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
        try {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            map.put(key, value);
        } finally {
            w.unlock();
            System.out.println(Thread.currentThread().getName() + " 写操作执行完毕..");
        }
    }
}



ublic class MyReadWriteLockTest {

    public static void main(String[] args) {
        writeWrite();

        writeRead();

        readRead();
    }

    //多个写,一个结束后再下一个
    /**
     Thread-0 写操作在执行..
     Thread-0 写操作执行完毕..
     Thread-2 写操作在执行..
     Thread-2 写操作执行完毕..
     Thread-1 写操作在执行..
     Thread-1 写操作执行完毕..
     */
    public static void writeWrite() {
        MyReadWriteLock myReadWriteLock1 = new MyReadWriteLock();

        new Thread(new Runnable() {
            @Override
            public void run() {
                myReadWriteLock1.put("key1", "value1");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                myReadWriteLock1.put("key2", "value2");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                myReadWriteLock1.put("key3", "value3");
            }
        }).start();
    }

    /**
     * main 写操作在执行..
     main 写操作执行完毕..
     Thread-0 读操作在执行..
     Thread-0 读操执行完毕..
     Thread-2 写操作在执行..
     Thread-0->value1
     Thread-2 写操作执行完毕..
     Thread-1 读操作在执行..
     Thread-3 读操作在执行..
     Thread-3 读操执行完毕..
     Thread-1 读操执行完毕..
     Thread-1->value2
     Thread-3->value2
     */
    //写和读,写的时候不能读,多个读的时候可以同时进行
    public static void writeRead() {
        MyReadWriteLock myReadWriteLock2 = new MyReadWriteLock();


        myReadWriteLock2.put("key1", "value1");

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"->"+myReadWriteLock2.get("key1"));
            }
        }).start();


        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"->"+myReadWriteLock2.get("key1"));
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                myReadWriteLock2.put("key1", "value2");
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"->"+myReadWriteLock2.get("key1"));
            }
        }).start();

    }



    //读读的时候,多个读可以同时进行
    public static void readRead() {
        MyReadWriteLock myReadWriteLock3 = new MyReadWriteLock();
        myReadWriteLock3.put("key1", "value1");

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName()+"->"+myReadWriteLock3.get("key1"));
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(myReadWriteLock3.get("key1"));
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(myReadWriteLock3.get("key1"));
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(myReadWriteLock3.get("key1"));
            }
        }).start();

        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println(myReadWriteLock3.get("key1"));
            }
        }).start();
    }

}


复制代码

4.ReentrantReadWriteLock源码解析

4.1 概述

  • 读写锁需要保存的状态
    • 写锁重入的次数
    • 读锁的个数
    • 每个读锁重入的次数

1111 1111 1111 1111-1111 1111 1111 1111

  • 前16位(高位)保存读锁个数,后16位(低位)保存写锁个数

int值就表示重入的次数

4.2 核心源码

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
        
    //1.私有readerLock和writerLock,通过下面的writeLock()和readLock()来访问
    private final ReentrantReadWriteLock.ReadLock readerLock;
    private final ReentrantReadWriteLock.WriteLock writerLock;
    final Sync sync;
    
    //1.1 构造方法
     public ReentrantReadWriteLock() {
        this(false);
    }
    
    //1.1.1 调用内部类ReadLock和WriteLock,此时创建了sync
     public ReentrantReadWriteLock(boolean fair) {
            sync = fair ? new FairSync() : new NonfairSync();
            readerLock = new ReadLock(this);
            writerLock = new WriteLock(this);
    }
    
        
    //通过该public方法来访问writeLock和readerLock    
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
    

    //2.ReadLock内部类,sync是外部的sync,是构造ReadLock的时候传进来的.
    public static class ReadLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -5992448646407690164L;
        private final Sync sync;

        //2.1 用的是外部内中的同步器sync       
        protected ReadLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }

       //2.2 读锁调调的是共享锁
        public void lock() {
            sync.acquireShared(1);
        }
    }
    
    //3.WriteLock内部类,sync是外部的sync,是构造WriteLock的时候传进来的.
     public static class WriteLock implements Lock, java.io.Serializable {
        private static final long serialVersionUID = -4992448646407690164L;
        private final Sync sync;

        protected WriteLock(ReentrantReadWriteLock lock) {
            sync = lock.sync;
        }
        //3.1 写锁调的是非共享锁
        public void lock() {
            sync.acquire(1);
        }
    }
    
    //Sync内部类实现类AQS
    abstract static class Sync extends AbstractQueuedSynchronizer {
        /*
         SHARED_SHIFT->16
         SHARED_UNIT->65536
         MAX_COUNT->65535
         EXCLUSIVE_MASK->65535
         int最大值为:-2147483648到2147483648(有符号),
         int无符号0~4,294,967,296(2^32),拆分为2部分最大值为65536(2^16)(65536*65536=4,294,967,296)
         1111 1111 1111 1111-1111 1111 1111 1111
         高位读锁,低位写锁,前16位保存读锁个数(65536),后16位保存写锁个数(65536)
        */
        static final int SHARED_SHIFT   = 16;
        static final int SHARED_UNIT    = (1 << SHARED_SHIFT);
        static final int MAX_COUNT      = (1 << SHARED_SHIFT) - 1;
        static final int EXCLUSIVE_MASK = (1 << SHARED_SHIFT) - 1;

        /** Returns the number of shared holds represented in count  */
        //拿到共享锁,读锁的个数,3个无符号右移()
        static int sharedCount(int c)    { return c >>> SHARED_SHIFT; }
        /** Returns the number of exclusive holds represented in count  */
        //独占锁,写锁的个数,
        static int exclusiveCount(int c) { return c & EXCLUSIVE_MASK; }
        
        //获取独占锁
        protected final boolean tryAcquire(int acquires) {}
        //释放独占锁
        protected final boolean tryRelease(int releases) {}
        //获取共享锁,这里面要先判断是否读锁,然后是否本线程,然后是其它线程,
        //还保存了每个读锁的次数及总次数
        protected final int tryAcquireShared(int unused) {}
        //释放共享锁
        protected final boolean tryReleaseShared(int unused) {}
        
    }
    
    //FairSync内部类
     static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }
    
    //NonfairSync内部类
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -8159625535654395037L;
        final boolean writerShouldBlock() {
            return false; // writers can always barge
        }
        final boolean readerShouldBlock() {
           return apparentlyFirstQueuedIsExclusive();
        }
    }

}
    
复制代码

4.3 核心总结:

  • 1.有WriteLock和ReadLock,都用的是Sync同步器,Sync 继承AbstractQueuedSynchronizer,也有公平和不公平
  • 2.WriteLock写锁调用的是Sync的排他锁tryAcquire,ReadLock调用的是Sync的共享锁tryAcquireShared,都记录了重入的次数
  • 3.private volatile int state;state的前16位(高位)保存读锁重入次数(65536),后16位(低位)保存写锁重入次数(65536)
  • 4.有内部类调用外部类的属性的操作.

5. ReentrantReadWriteLock锁降级

5.1 概述

  • 锁降级是指写锁降级为读锁。锁降级指的是写锁降级成为读锁。锁降级是指把持住当前拥有的写锁的同时,再获取到读锁,随后释放写锁的过程

  • 锁升级 ReentrantReadWriteLock不支持锁升级

把读锁升级为写锁,在读锁没有释放的时候,获取到写锁,再释放读锁

5.2 官方示例

 class CachedData {
   Object data;
   volatile boolean cacheValid;
   final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();

   void processCachedData() {
     rwl.readLock().lock();
     if (!cacheValid) {
        // Must release read lock before acquiring write lock
        rwl.readLock().unlock();
        rwl.writeLock().lock();
        try {
          // Recheck state because another thread might have
          // acquired write lock and changed state before we did.
          if (!cacheValid) {
            data = ...
            cacheValid = true;
          }
          // Downgrade by acquiring read lock before releasing write lock
          rwl.readLock().lock();
        } finally {
          rwl.writeLock().unlock(); // Unlock write, still hold read
        }
     }

     try {
       use(data);
     } finally {
       rwl.readLock().unlock();
     }
   }
 }
复制代码

代码中声明了一个volatile类型的cacheValid变量,保证其可见性。首先获取读锁,如果cache不可用,则释放读锁,获取写锁,在更改数据之前,再检查一次cacheValid的值,然后修改数据,将cacheValid置为true,然后在释放写锁前获取读锁;此时,cache中数据可用,处理cache中数据,最后释放读锁。这个过程就是一个完整的锁降级的过程,目的是保证数据可见性,如果当前的线程C在修改完cache中的数据后,没有获取读锁而是直接释放了写锁,那么假设此时另一个线程T获取了写锁并修改了数据,那么C线程无法感知到数据已被修改,则数据出现错误。如果遵循锁降级的步骤,线程C在释放写锁之前获取读锁,那么线程T在获取写锁时将被阻塞,直到线程C完成数据处理过程,释放读锁。

5.3 运用举例


import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public class ReadWriteDegrade {

	private Map<String, Object> map = new HashMap<>();

	private ReadWriteLock rwl = new ReentrantReadWriteLock();

	private Lock r = rwl.readLock();
	private Lock w = rwl.writeLock();

	private volatile boolean isUpdate;

	//又有读操作又有写操作的例子
	public void readWrite() {
		r.lock(); // 为了保证isUpdate能够拿到最新的值
		if (isUpdate) {
			r.unlock();
			w.lock();
			map.put("xxx", "xxx");
			r.lock();
			w.unlock();
		}

		Object obj = map.get("xxx");

		System.out.println(obj);
		r.unlock();

	}

	public Object get(String key) {
		r.lock();
		System.out.println(Thread.currentThread().getName() + " 读操作在执行..");
		try {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			return map.get(key);
		} finally {
			r.unlock();
			System.out.println(Thread.currentThread().getName() + " 读操执行完毕..");
		}
	}

	public void put(String key, Object value) {
		w.lock();
		System.out.println(Thread.currentThread().getName() + " 写操作在执行..");
		try {
			try {
				Thread.sleep(3000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			map.put(key, value);
		} finally {
			w.unlock();
			System.out.println(Thread.currentThread().getName() + " 写操作执行完毕..");
		}
	}

}

复制代码

参考文章: 读写锁ReentrantReadWriteLock之锁降级

6. StampedLock(JDK8以后)

6.1 概述

StampedLock是ReentrantReadWriteLock升级.
/stæmpt/

6.1.1 ReentrantReadWriteLock的缺点

  • 读写锁(ReentrantReadWriteLock)是互斥的-读写互斥,写写互斥.读读不互斥.
  • 由于读比较多,可能造成写线程饥饿(抢占不到资源),
  • ReentrantReadWriteLock可以用的公平锁. 但是公平锁相对非公平锁性能低.
  • StampedLock 提高读写互斥的性能,读锁并不会阻塞写锁,同时还提升性能.

6.1.2 StampedLock优点原理

实现类读锁并不会阻塞写锁,那怎么保证读写的一致性呢?--如果在读的时候发生写锁,则重新读. 怎么知道 读的时候正好发生了锁呢?- 有一个票据.如果乐观锁就读写不互斥. 如果拿到的是悲观锁则读写互斥.

6.2 官方示例

docs.oracle.com/javase/8/do…

lass 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); //释放读锁或写锁
     }
   }
 }
复制代码

6.3 代码示例


import java.util.concurrent.locks.StampedLock;

public class StampedLockThread {

    private int balance;  //余额

    private StampedLock lock = new StampedLock();

    //带条件的读写的问题
    public void conditionReadWrite(int value) {
        // 首先判断balance的值是否符合更新的条件,余额只能为正数.
        long stamp = lock.readLock();//获取票据
        try{
            //多次循环执行(随便写的一个条件)
            while (balance > 0) {
                long writeStamp = lock.tryConvertToWriteLock(stamp);//读锁,如果条件成立可以转换为写锁.
                if (writeStamp != 0) { // 成功转换成为写锁
                    stamp = writeStamp;
                    balance += value;
                    break;
                } else {
                    // 没有转换成写锁,这里需要首先释放读锁,然后再拿到写锁
                    lock.unlockRead(stamp);
                    // 获取写锁
                    stamp = lock.writeLock();//更新票据
                }
            }
        }finally {
            lock.unlock(stamp);//锁都释放掉
        }
    }

    //乐观锁读
    public void optimisticRead() {
        long stamp = lock.tryOptimisticRead();
        try {
            int c = balance;
            // 这里可能会出现了写操作,因此要进行判断,如果发生了有写操作,需要重新读数据.
            if (!lock.validate(stamp)) {
                // 要从新读取
                long readStamp = lock.readLock();
                c = balance;
                stamp = readStamp;
            }
            ///
        }
        finally {
            lock.unlockRead(stamp);
        }
    }

    //读-获取乐观锁
    public void read2() {
        long stamp = lock.readLock();
        try {
            lock.tryOptimisticRead();   //获取乐观锁
            int c = balance;
            System.out.print("read2-stamp:" + stamp + ",balance:" + balance);
            // ...
        }finally {
            lock.unlockRead(stamp);
        }
    }


    //读-和ReentrantReadWriteLock一样
    public void read() {
        long stamp = lock.readLock();
        try {
            int c = balance;
            System.out.print("read-stamp:"+stamp+",balance:"+balance);
            // ...
        }finally {
            lock.unlockRead(stamp);
        }
    }

    //写-和ReentrantReadWriteLock一样
    public void write(int value) {
        long stamp = lock.writeLock();  //写锁,有返回值,
        try {
            System.out.print("write-stamp:"+stamp+",value:"+value);
            balance += value;
            System.out.println(",balance:"+balance);
        }finally {
            lock.unlockWrite(stamp);        //解锁
        }
    }
}

复制代码

6.4 核心源码

package java.util.concurrent.locks;
public class StampedLock implements java.io.Serializable {

    /**
     * Exclusively acquires the lock, blocking if necessary
     * until available.
     *
     * @return a stamp that can be used to unlock or convert mode
     */
    //拿到写锁才返回,票据就是返回值即注释中说的stamp 
    public long writeLock() {
        long s, next;  // bypass acquireWrite in fully unlocked case only
        return ((((s = state) & ABITS) == 0L &&
                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                next : acquireWrite(false, 0L));
    }
    
    
     /**
     * Exclusively acquires the lock if it is immediately available.
     *
     * @return a stamp that can be used to unlock or convert mode,
     * or zero if the lock is not available
     */
    //立刻返回,票据就是返回值即注释中说的stamp  ,拿不到就返回0
     public long tryWriteLock() {
        long s, next;
        return ((((s = state) & ABITS) == 0L &&
                 U.compareAndSwapLong(this, STATE, s, next = s + WBIT)) ?
                next : 0L);
    }
    
    
    
      /**
     * Non-exclusively acquires the lock, blocking if necessary
     * until available.
     *
     * @return a stamp that can be used to unlock or convert mode
     */
    //非独占锁
    public long readLock() {
        long s = state, next;  // bypass acquireRead on common uncontended case
        return ((whead == wtail && (s & ABITS) < RFULL &&
                 U.compareAndSwapLong(this, STATE, s, next = s + RUNIT)) ?
                next : acquireRead(false, 0L));
    }

    /**
     * Non-exclusively acquires the lock if it is immediately available.
     *
     * @return a stamp that can be used to unlock or convert mode,
     * or zero if the lock is not available
     */
     //非独占锁,立刻返回,,票据就是返回值即注释中说的stamp ,拿不到就返回0
    public long tryReadLock() {
        for (;;) {
            long s, m, next;
            if ((m = (s = state) & ABITS) == WBIT)
                return 0L;
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, next = s + RUNIT))
                    return next;
            }
            else if ((next = tryIncReaderOverflow(s)) != 0L)
                return next;
        }
    }
    
    
    /**
     * Returns a stamp that can later be validated, or zero
     * if exclusively locked.
     *
     * @return a stamp, or zero if exclusively locked
     */
    //获取乐观的读锁
    public long tryOptimisticRead() {
        long s;
        return (((s = state) & WBIT) == 0L) ? (s & SBITS) : 0L;
    }


  /**
     * Returns true if the lock has not been exclusively acquired
     * since issuance of the given stamp. Always returns false if the
     * stamp is zero. Always returns true if the stamp represents a
     * currently held lock. Invoking this method with a value not
     * obtained from {@link #tryOptimisticRead} or a locking method
     * for this lock has no defined effect or result.
     *
     * @param stamp a stamp
     * @return {@code true} if the lock has not been exclusively acquired
     * since issuance of the given stamp; else false
     */
    //根据票据的值来返回是否修改过 
    public boolean validate(long stamp) {
        U.loadFence();
        return (stamp & SBITS) == (state & SBITS);
    }


    //释放写锁
    public void unlockWrite(long stamp) {
        WNode h;
        if (state != stamp || (stamp & WBIT) == 0L)
            throw new IllegalMonitorStateException();
        state = (stamp += WBIT) == 0L ? ORIGIN : stamp;
        if ((h = whead) != null && h.status != 0)
            release(h);
    }

    //释放读锁
    public void unlockRead(long stamp) {
        long s, m; WNode h;
        for (;;) {
            if (((s = state) & SBITS) != (stamp & SBITS) ||
                (stamp & ABITS) == 0L || (m = s & ABITS) == 0L || m == WBIT)
                throw new IllegalMonitorStateException();
            if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        release(h);
                    break;
                }
            }
            else if (tryDecReaderOverflow(s) != 0L)
                break;
        }
    }
    
    
    //释放读锁或写锁
    public void unlock(long stamp) {
        long a = stamp & ABITS, m, s; WNode h;
        while (((s = state) & SBITS) == (stamp & SBITS)) {
            if ((m = s & ABITS) == 0L)
                break;
            else if (m == WBIT) {
                if (a != m)
                    break;
                state = (s += WBIT) == 0L ? ORIGIN : s;
                if ((h = whead) != null && h.status != 0)
                    release(h);
                return;
            }
            else if (a == 0L || a >= WBIT)
                break;
            else if (m < RFULL) {
                if (U.compareAndSwapLong(this, STATE, s, s - RUNIT)) {
                    if (m == RUNIT && (h = whead) != null && h.status != 0)
                        release(h);
                    return;
                }
            }
            else if (tryDecReaderOverflow(s) != 0L)
                return;
        }
        throw new IllegalMonitorStateException();
    }

    //转换为读锁
    public long tryConvertToReadLock(long stamp) {
    }
    
    //转换为写锁
    public long tryConvertToWriteLock(long stamp) {
    }
    
}
复制代码

7.ReentrantReadWriteLock,StampedLock,synchronized对比总结

  • StampedLock要比ReentrantReadWriteLock更加廉价,也就是消耗比较小。
  • synchronized是在JVM层面上实现的,不但可以通过一些监控工具监控synchronized的锁定,而且在代码执行时出现异常,JVM会自动释放锁定;
  • ReentrantLock、ReentrantReadWriteLock,、StampedLock都是对象层面的锁定,要保证锁定一定会被释放,就必须将unLock()放到finally{}中;
  • StampedLock 对吞吐量有巨大的改进,特别是在读线程越来越多的场景下;
  • StampedLock有一个复杂的API,对于加锁操作,很容易误用其他方法;
  • 当只有少量竞争者的时候,synchronized是一个很好的通用的锁实现;
  • 当线程增长能够预估,ReentrantLock是一个很好的通用的锁实现;
  • StampedLock 可以说是Lock的一个很好的补充,吞吐量以及性能上的提升足以打动很多人了,但并不是说要替代之前Lock的东西,毕竟他还是有些应用场景的,起码API比StampedLock容易入手,
文章分类
后端