多线程之锁原理及AQS

  1. 停车位的场景
  2. Locks包
  3. AQS抽象队列同步器

1 停车位的场景

  • 场景:土豪小区车位紧张,只有0357车位还未出售,此时,大家都想买0357车位。

买了0357车位,才能在这儿停车(加锁),跟小区签订合同后,才具备停车到0357的资格。会有一把车位锁,来象征你的0357车位停车资格。

2 Locks包

  1. Locks包层次结构
  2. Lock接口
  3. ReentrantLock
  4. ReadWriteLock
  5. synchronized vs Lock
2.1 类层次结构

在这里插入图片描述
在这里插入图片描述

2.2 Lock接口
方法签名描述
void lock()获取锁(不死不休)
boolean tryLock()获取锁(浅尝辄止)
boolean tryLock(long time, TimeUnit unit)获取锁(过时不候)
void lockInterruptibly()获取锁(任人摆布)
void unlock()释放锁
Condition newCondution()
2.3 ReentrantLock
public class Demo2 Reentrant{
	static Lock lock = new ReentrantLock();
	public static void main(String[] args){
		System.out.println("get lock 1...\n");
		lock.lock(); //当前线程已获得锁
		
		System.out.println("get lock 2...\n");
		lock.lock(); //再次获取,是否能成功
		
		System.out.println("here i am ... \n");

		System.out.println("unlock 1...\n");
		lock.unlock();
	
		System.out.println("unlock 2...\n");
		lock.unlock();

		System.out.println("unlock 3...\n");
		lock.unlock(); //第三次释放锁,报错
	}
}
owner = null, count = 0
owner = mainThread, count = 1
owner = mainThread, count = 2
owner = mainThread, count = 1
owner null, count = 0
illegalMonitorStateException
2.4 ReadWriteLock
  • 概念

维护一对关联锁,一个只用于读操作,一个只用于写操作;
读锁可以有多个读线程同时持有,写锁是排他的。同一时间,两把锁不能被不同线程持有。

  • 适用场景

适合读取操作多于写入操作的场景,改进互斥锁的性能,比如:集合的并发线程安全性改造、缓存组件。

  • 锁降级

指的是写锁降级成为读锁,即持有写锁的同时,再获取读锁,随后释放写锁的过程。写锁是线程独占,读锁是线程共享,所以写->读是降级(读->写,是不能实现的)

ReadWriteLock用一个int存储了两个count值,readCount和writeCount,前16位表示readCount,后16位表示writeCount。

2.5 synchronized vs Lock
Synchronized
  • 优点
    • 使用简单,语义清晰
    • 由JVM提供,提供了多种优化方案(锁粗化、锁消除、偏向锁、轻量级锁)
    • 锁的释放由虚拟机来完成,不用人工干预,也降低了死锁的可能
  • 缺点
    • 无法实现一些锁的高级功能,如公平锁、中断锁、超时锁、读写锁、共享锁等
Lock
  • 优点
    • 所有synchronized的缺点
    • 可以实现更多功能,让synchronized缺点更多
  • 缺点
    • 需手动释放锁unlock,使用不当可能造成死锁

结论:synchronized是卡片机,Lock是单反。

3 AQS抽象队列同步器

juc包下很多API都是基于AQS来实现加锁和解锁的,AQS是juc包的基础类。

AQS中的主要方法

  • acquire
  • tryAcquire
  • release
  • tryRelease
  • acquireShared
  • tryAcquireShared
  • releaseShared
  • tryReleaseShared

AQS中的字段

  • int state:核心变量,代表了加锁的状态
  • Thread Owner:记录当前加锁的线程
  • Queue

AQS中的Queue

head
node
node
tail
  1. 线程1通过CAS修改state,将0变为1;
  2. 线程2过来修改state时,发现值不为0且加锁线程不是自己;
  3. 线程2将自己放入AQS的等待队列
  4. 线程1释放锁,修改state为0,将加锁线程置为null;
  5. 等待队列的队头唤醒线程2重新尝试加锁。

以下来自大佬中华石杉画的图,画的很清晰,一目了然
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

参考文章

大白话聊聊Java并发面试问题之谈谈你对AQS的理解?【石杉的架构笔记】
网易云课堂《Java高级开发工程师》

结语

本人所有博客仅用于学习记录,不做任何商业用途,如涉及侵权,还请联系删除,感谢阅读,欢迎留言,一起进步~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值