并发编程—独占锁和共享锁
大家好,我是宇哥
锁的独占与共享
java并发包提供的加锁模式分为独占锁和共享锁。
独占锁模式下,每次只能有一个线程能持有锁,ReentrantLock就是以独占方式实现的互斥锁。
共享锁:则允许多个线程同时获取锁,并发访问 共享资源,如:ReadWriteLock。AQS的内部类Node定义了两个常量SHARED和EXCLUSIVE,他们分别标识 AQS队列中等待线程的锁获取模式。
很显然,独占锁是一种悲观保守的加锁策略,它避免了读/读冲突,如果某个只读线程获取锁,则其他读线程都只能等待,这种情况下就限制了不必要的并发性,因为读操作并不会影响数据的一致性。共享锁则是一种乐观锁,它放宽了加锁策略,允许多个执行读操作的线程同时访问共享资源。java的并发包中提供了ReadWriteLock,读-写锁。它允许一个资源可以被多个读操作访问,或者被一个 写操作访问,但两者不能同时进行。
独占锁与共享锁的区别:
独占功能
当锁被头节点获取后,只有头节点获取锁,其余节点的线程继续沉睡,等待锁被释放后,才会唤醒下一个节点的线程。共享功能
只要头节点获取锁成功,就在唤醒自身节点对应的线程的同时,继续唤醒AQS队列中的下一个节点的线程,每个节点在唤醒自身的同时还会唤醒下一个节点对应的线程,以实现共享状态的“向后传播”,从而实现共享功能。
AQS提供的模板方法
AQS提供了独占锁和共享锁必须实现的方法,具有独占锁功能的子类,它必须实现tryAcquire、tryRelease、isHeldExclusively等;共享锁功能的子类,必须实现tryAcquireShared和tryReleaseShared等方法,带有Shared后缀的方法都是支持共享锁加锁的语义。Semaphore是一种共享锁,ReentrantLock是一种独占锁。
独占锁获取锁时,设置节点模式为Node.EXCLUSIVE
public final void acquire(int arg) {if (!tryAcquire(arg) &&acquireQueued(addWaiter(Node.EXCLUSIVE), arg))selfInterrupt();}
共享锁获取锁,节点模式则为Node.SHARED
private void doAcquireShared(int arg) {final Node node = addWaiter(Node.SHARED);boolean failed = true;.....}
对ConditionObject的认识
ReentrantLock是独占锁,而且AQS的ConditionObject只能与ReentrantLock一起使用,它是为了支持条件队列的锁更方便。ConditionObject的signal和await方法都是基于独占锁的,如果线程非锁的独占线程,则会抛出IllegalMonitorStateException。例如signalAll源码:
public final void signalAll() {if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignalAll(first);}
实现-独占锁
public class OptimisticExclusiveLock {/*** 独占锁标记 true 锁不可用 false 锁可用*/private AtomicBoolean state = new AtomicBoolean(false);List<Thread> queue = new ArrayList<Thread>();//阻塞队列public boolean lock() {if (!state.get()&&state.compareAndSet(false, true)) {//取锁成功不会阻塞,程序会继续执行return true; // 利用CAS} else {queue.add(Thread.currentThread());//加入阻塞队列LockSupport.park();//阻塞线程return false;}}public boolean unLock() {if (state.get()) {queue.remove(Thread.currentThread());//从队列里移除if (state.compareAndSet(true, false)) {// 利用CASif(!queue.isEmpty()){LockSupport.unpark(queue.get(0));//唤醒第一个等待线程}return true;}return false;} else {return false;}}}
实现-共享锁
public class OptimisticExclusiveLockTest {public static OptimisticExclusiveLock lock = new OptimisticExclusiveLock(); // 独占锁public static volatile int i = 0; // 保证可见性public class Task implements Runnable {@Overridepublic void run() {while (true) {try {lock.lock();//加锁i += 2;System.out.println("thread name:" + Thread.currentThread().getName() + " i=" + i);} finally {lock.unLock();//释放锁try {Thread.currentThread().sleep(10);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}}}}}
测试代码
public void runTask() {for (int i = 0; i < 100; i++) {Thread t = new Thread(new Task(), "thread" + i);t.start();}}public static void main(String[] args) {OptimisticExclusiveLockTest test = new OptimisticExclusiveLockTest();test.runTask();}
快乐三连击:【点赞,在看,分享】
