JUC中几种锁的原理剖析

LockSupport工具类

底层是Unsafe类实现的,主要作用是挂起(park()方法)和唤醒(unpark()方法)线程,看起来和wait()与notify()差不多,不过还有一些区别:park()不需要获取对象的锁。
LockSupport类与每个使用它的线程都会关联一个许可证。默认情况下,调用LockSupport类的方法的线程是不持有许可证的,这也就导致直接调用park()方法的线程会被禁用。那么怎么获取许可证呢?就要用到unpark()方法。
jdk官方文档中是这么写的:
在这里插入图片描述
在这里插入图片描述
还有一点区别,就是中断时park()不会抛出InterruptedException异常,park()和unpark()的调用顺序反了也不会有问题。
到这里我们就有一个感性的认识,就是park()和unpark()使用起来比较灵活,关于park()和unpark()的具体细节可以参考这一篇博客, 写得十分详细。本文就不再展开啦。
接下来进入重头戏:所有锁的底层实现,抽象同步队列,AbstractQueuedSynchronizer(AQS)。

AQS

AQS的数据结构十分经典。它是一个FIFO的双向队列,底层是链表,队列元素的类型为Node。它有两个十分重要的成员变量:int类型的状态信息state,和内部类ConditionObject(条件变量,这么说是不是大家就开始理解了呀)

前面说到所有的锁都是用AQS实现的,这个**state就针对不同种类的锁而有不同的含义。**对于ReentrantLock的实现而言,state是当前线程获取锁的可重入次数;对于读写锁ReentrantReadWriteLock来说,state的高16位表示获取该读锁的次数,低16位表示获取到写锁的线程的可重入次数;对于Semaphore而言,state是当前可用信号的个数;对CountDownLatch而言,state就是计数器当前的值。

那么ConditionObject呢?它是一个条件变量,用来结合锁实现线程同步。 我们在使用synchronized的时候,有时需要锁住一个Object类型的变量,用来进行后续操作,这里的条件变量就可以看作是那个Object变量,并且它还有着其他的好处:可以创建多个条件变量,每个条件变量都给你维护了一个单向链表队列,用来存放调用条件变量的await()方法后被阻塞的线程。

线程同步的关键是对状态值state进行操作。根据state是否属于某个线程,操作state的方式分为独占方式和共享方式。 独占方式的线程获取到了资源,就会给这个资源打上标记,标志着自己获取到了这个资源,其他线程再尝试操作state时就会发现自己不是资源的持有者,于是被阻塞。而共享方式的线程就比较佛系,用CAS方式获取资源,当一个线程获取到了资源后,另一个线程再去获取,发现当前资源还能满足它的需要,就CAS获取资源,资源数不满足则被放入阻塞队列中。

到这里我们可以看出,继承了AQS类的子类起码要实现这么几个方法:isHeldExclusively(),用来判断是独占还是共享;acquire()和release()(独占锁)/acquireShared()和releaseShared()(共享锁),用来实现具体的获取资源和释放资源。这些方法AQS统统没有提供,全都要靠子类自己实现。

再来说说Condition条件变量。关于它挂起线程和唤醒线程的方式,写了一个小例子:

public static void main(String args[]) {
   
		ReentrantLock lock = new ReentrantLock();  //创建了一个独占锁
		Condition condition = lock.newCondition();  //创建了一个条件变量
		
		//创建两个线程
		//一个await,一个signal
		Thread t1 = new Thread(new Runnable() {
   
			
			@Override
			public void run() {
   
				// TODO Auto-generated method stub
				lock.lock();
				try {
   
					System.out.println("begin await");
					condition.await();
					System.out.println("end await");
				}catch(Exception e) {
   
					e.printStackTrace();
				}finally {
   
					lock.unlock();
				}
			}
		});
		
		Thread t2 = new Thread(new Runnable() {
   
			
			@Override
			public void run() {
   
				// TODO Auto-generated method stub
				lock.lock();
				try {
   
					System.out.println("begin signal");
					condition.signal();
					System.out.println
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值