有哪些锁,是怎么样的**

偏向锁/轻量级锁/重量级锁;

可重入锁/非可重入锁;

共享锁/独占锁;
公平锁/非公平锁;

悲观锁/乐观锁;

自旋锁/非自旋锁;

可中断锁/不可中断锁。
**

悲观锁和乐观锁的本质

悲观锁:悲观锁比较悲观,认为总有其他线程来争抢,所以锁住,一定保障结果的正确
乐观锁:乐观锁比较乐观,天真的认为自己工作的时候,不会有别的线程干扰,如果与被其他线程抢先执行了,会放弃这次操作,选择报错或者重试等机制(CAS)
经典案例
悲观锁:synchronized 关键字和 Lock 接口 适合写多的场景
乐观锁:原子类(CAS AtomicInteger) 适合读多写少的场景
大喜大悲:数据库 两个都有

synchronized 背后的“monitor 锁”

monitorenter和monitorexit
锁的对象持有一个monitor对象,对象保存持有锁的对象地址,
可重入,有一个计数器

synchronized 和 Lock 孰优孰劣

相同点

  1. 都是用来保护资源线程安全的。
  2. 都可以保证可见性。
  3. 都拥有可重入的特点。

不同点

  1. 用法区别 synchronized (可方法,this,代码块,隐式枷锁)
  2. 加解锁顺序不同
  3. synchronized 锁不够灵活 (Lock可中断,可超时)
  4. synchronized 锁只能同时被一个线程拥有,但是 Lock 锁没有这个限制
  5. 原理区别

synchronized 是内置锁,由 JVM 实现获取锁和释放锁的原理,还分为偏向锁、轻量级锁、重量级锁。
Lock 根据实现不同,有不同的原理,例如 ReentrantLock 内部是通过 AQS 来获取和释放锁的。

  1. 是否可以设置公平/非公平
  2. 性能区别

如何选择
一句话,synchronized 优先,不适用的才用Lock

  1. 如果能不用最好既不使用 Lock 也不使用 synchronized。因为在许多情况下你可以使用 java.util.concurrent 包中的机制,它会为你处理所有的加锁和解锁操作,也就是推荐优先使用工具类来加解锁。
  2. 如果 synchronized 关键字适合你的程序, 那么请尽量使用它,这样可以减少编写代码的数量,减少出错的概率。因为一旦忘记在 finally 里 unlock,代码可能会出很大的问题,而使用 synchronized 更安全。
  3. 如果特别需要 Lock 的特殊功能,比如尝试获取锁、可中断、超时功能等,才使用 Lock。

公平锁和非公平锁


锁 - 图1

读写锁 ReadWriteLock

读读共享、其他都互斥(写写互斥、读写互斥、写读互斥)

读锁应该插队吗

读锁插队策略
公平锁:不允许插队
非公平锁:写可插队,读根据对应策略选择
允许插队:可能造成需要写锁的进程 饥饿
不允许插入:不会饥饿,ReentrantReadWriteLock 的默认选择

锁的升降级

写锁可以降级读锁,写锁完成写工作后,读锁可保障正常允许,锁细化
读锁不能升级为写锁,多个读锁升级写锁吗,会造成死锁

自旋锁

阻塞和唤醒线程都是需要高昂的开销的,如果同步代码块中的内容不复杂,那么可能转换线程带来的开销比实际业务代码执行的开销还要大
案例:AtomicLong,并发度不是特别高的,临界区比较短小的场景
缺点:
如果一直不能得到锁,白白让费cpu资源,随着时间增加,消耗资源会超过线程切换

JVM 对锁进行了哪些优化

  1. 自适应的自旋锁:富者更富,穷者更穷
  2. 锁消除
  3. 锁粗化
  4. 偏向锁/轻量级锁/重量级锁