有哪些锁,是怎么样的**
偏向锁/轻量级锁/重量级锁;
可重入锁/非可重入锁;
共享锁/独占锁;
公平锁/非公平锁;
悲观锁/乐观锁;
自旋锁/非自旋锁;
悲观锁和乐观锁的本质
悲观锁:悲观锁比较悲观,认为总有其他线程来争抢,所以锁住,一定保障结果的正确
乐观锁:乐观锁比较乐观,天真的认为自己工作的时候,不会有别的线程干扰,如果与被其他线程抢先执行了,会放弃这次操作,选择报错或者重试等机制(CAS)
经典案例
悲观锁:synchronized 关键字和 Lock 接口 适合写多的场景
乐观锁:原子类(CAS AtomicInteger) 适合读多写少的场景
大喜大悲:数据库 两个都有
synchronized 背后的“monitor 锁”
monitorenter和monitorexit
锁的对象持有一个monitor对象,对象保存持有锁的对象地址,
可重入,有一个计数器
synchronized 和 Lock 孰优孰劣
相同点
- 都是用来保护资源线程安全的。
- 都可以保证可见性。
- 都拥有可重入的特点。
不同点
- 用法区别 synchronized (可方法,this,代码块,隐式枷锁)
- 加解锁顺序不同
- synchronized 锁不够灵活 (Lock可中断,可超时)
- synchronized 锁只能同时被一个线程拥有,但是 Lock 锁没有这个限制
- 原理区别
synchronized 是内置锁,由 JVM 实现获取锁和释放锁的原理,还分为偏向锁、轻量级锁、重量级锁。
Lock 根据实现不同,有不同的原理,例如 ReentrantLock 内部是通过 AQS 来获取和释放锁的。
- 是否可以设置公平/非公平
- 性能区别
如何选择
一句话,synchronized 优先,不适用的才用Lock
- 如果能不用最好既不使用 Lock 也不使用 synchronized。因为在许多情况下你可以使用 java.util.concurrent 包中的机制,它会为你处理所有的加锁和解锁操作,也就是推荐优先使用工具类来加解锁。
- 如果 synchronized 关键字适合你的程序, 那么请尽量使用它,这样可以减少编写代码的数量,减少出错的概率。因为一旦忘记在 finally 里 unlock,代码可能会出很大的问题,而使用 synchronized 更安全。
- 如果特别需要 Lock 的特殊功能,比如尝试获取锁、可中断、超时功能等,才使用 Lock。
公平锁和非公平锁

读写锁 ReadWriteLock
读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)
读锁应该插队吗
读锁插队策略
公平锁:不允许插队
非公平锁:写可插队,读根据对应策略选择
允许插队:可能造成需要写锁的进程 饥饿
不允许插入:不会饥饿,ReentrantReadWriteLock 的默认选择
锁的升降级
写锁可以降级读锁,写锁完成写工作后,读锁可保障正常允许,锁细化
读锁不能升级为写锁,多个读锁升级写锁吗,会造成死锁
自旋锁
阻塞和唤醒线程都是需要高昂的开销的,如果同步代码块中的内容不复杂,那么可能转换线程带来的开销比实际业务代码执行的开销还要大
案例:AtomicLong,并发度不是特别高的,临界区比较短小的场景
缺点:
如果一直不能得到锁,白白让费cpu资源,随着时间增加,消耗资源会超过线程切换
JVM 对锁进行了哪些优化
- 自适应的自旋锁:富者更富,穷者更穷
- 锁消除
- 锁粗化
- 偏向锁/轻量级锁/重量级锁
