并发编程—独占锁和共享锁
Posted 与你同在架构之路
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程—独占锁和共享锁相关的知识,希望对你有一定的参考价值。
大家好,我是宇哥
锁的独占与共享
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)) {// 利用CAS
if(!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 {
@Override
public 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 block
e.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();
}
快乐三连击:【点赞,在看,分享】
以上是关于并发编程—独占锁和共享锁的主要内容,如果未能解决你的问题,请参考以下文章