乐观锁

乐观锁的思想认为读多写少,每次拿数据的时候会认为别人不会修改,所以不会上锁。

在更新的时候会判断一下在此期间别人有没有更新这个数据,采取的是写时限读出当前的版本号之后加锁的操作。

通过CAS操作实现,CAS是一种更新的原子操作,比较当前值和传入值是否一样,一样则更新,否则失败。

悲观锁

悲观锁认为写多于读,并且遇到并发的可能性比较高,每次读写数据都会上锁,这样别人想读写数据就会block。

java的悲观锁就是Synchronized, AQS 框架下的锁则是先尝试CAS 乐观锁取获取锁,获取不到才会转换为悲观锁。例如RetreenLock.

自旋锁

如果持有锁的线程能够在很短的时间内释放资源,那么那些等待竞争锁的线程就不需要做内核态和用户态之间的切换即进入阻塞挂起状态,只需要等一等(自旋),等待持有锁的线程释放锁后即可立即获取锁,这样就避免了用户线程和内核切换的消耗。

线程自旋是需要消耗CPU的,说白了就是让CPU做无用功,所以会设定一个自旋最大等待时间,以免造成资源过度浪费。时间到了就会停止自旋,并进入阻塞状态。

优缺点:

  1. 减少线程阻塞(适用于锁竞争不激烈,占用锁时间段的代码块)
  2. 锁竞争激烈,自旋锁会造成CPU浪费。

java 怎么查询对象的锁信息_java同步锁

synchronized可以把任意一个非NULL的对象当做锁。他属于独占式的悲观锁,同时属于可重入锁。

作用范围:

  1. 作用于方法是,锁住的是对象的实例(this);
  2. 作用于静态方法时,锁住的是Class实例,因为Class相关数据存储在永久代java 怎么查询对象的锁信息_公平锁_02(jdk1.8是metaspace),永久代是全局共享的,因此静态方法锁想当与类的一个全局锁,锁所有调用该方法的进程。
  3. java 怎么查询对象的锁信息_java 怎么查询对象的锁信息_03作用于一个对象实例时,锁住的是所有以该对象为锁的代码块。他有多个队列,当多个线程一起访问某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。

java 怎么查询对象的锁信息_java核心组件

  1. java 怎么查询对象的锁信息_并发编程_05: 哪些调用wait方法被阻塞的线程被放置在这里。
  2. java 怎么查询对象的锁信息_并发编程_06: 竞争队列,所有请求锁的线程首先被放在这个竞争队列中。
  3. java 怎么查询对象的锁信息_多线程_07: Contention List 中那些有资格成为候选资源的线程被移动到Entry List中。
  4. java 怎么查询对象的锁信息_多线程_08:任意时刻,最多只有一个线程正在竞争锁资源,该线程被称为OnDeck;
  5. java 怎么查询对象的锁信息_java 怎么查询对象的锁信息_09:当前获取到所有资源的线程称为Owner;
  6. java 怎么查询对象的锁信息_java 怎么查询对象的锁信息_10: 当前释放锁的线程。

java 怎么查询对象的锁信息_java实现

java 怎么查询对象的锁信息_多线程_12

  • java 怎么查询对象的锁信息_多线程_13是非公平锁。
  1. Synchronized在线程进入ContentionList时,等待的线程会线城市自旋获取锁,如果获取不到就进入ContentionList,这明显对于已经进入队列的线程是不公平的。
  2. 还有一个不公平的事就是自旋获取锁的线程可能直接抢占OnDeck线程的锁资源。
  • 每个对象都有一个monitor对象,加锁就是在竞争monitor对象,代码块加锁是在前后分别加上java 怎么查询对象的锁信息_java_14java 怎么查询对象的锁信息_java 怎么查询对象的锁信息_15指令实现的,方法加锁是通过一个标记位来判断的。
  • 锁膨胀:锁可以从偏向锁升级到轻量级锁,再升级到重量级锁。

ReentrantLock

ReentrantLock继承了接口Lock并实现了接口中定义的方法,它是一种可重用锁,不仅能完成synchronized所能完成的工作外,还提供了响应中断锁、可轮询锁请求、定时锁等避免多线程死锁的方法。

Lock接口的主要方法:

详见jdk8文档

非公平锁与公平锁

非公平锁

JVM 按随机、就近原则分配锁的机制则成为不公平锁,ReentrantLock在构造函数中提供了是否公平锁的初始化方式,默认为非公平锁。非公平锁实际执行的效率远远高于公平锁。

公平锁是指锁的分配机制是公平的,通常对锁提出获取请求的线程会先被分配到锁,ReentrantLock在构造函数中提供了是否公平锁的初始化方式定义公平锁。

ReentrantLock与synchronized

  1. ReentrantLock通过方法lock()与unlock()来进行加锁与解锁操作,手动解锁;必须在finally控制块中进行解锁操作。
  2. synchronized会被jvm自动解锁。
  3. ReentranLock相比于synchronized的优势是可中断、公平锁、多个锁。

Condition 和Object类锁方法的区别

  1. Condition类的awiat方法和Object类的wait方法等效。
  2. Condition类的signal方法和Object类的notigy方法等效。
  3. Condition类的signalAll方法和Object类的notigyAll方法等效。
  4. ReentrantLock类可以唤醒指定条件的线程,而object的唤醒是随机的。

tryLock和lock和lockInterruptibly的区别

  1. java 怎么查询对象的锁信息_公平锁_16能获得锁就返回true,不能就立即返回false;
  2. java 怎么查询对象的锁信息_多线程_17,可以增加时间限制,如果超过该时间段还没有获得锁,返回false
  3. java 怎么查询对象的锁信息_java 怎么查询对象的锁信息_18能获得锁就返回true,不能的话就一直等待锁
  4. java 怎么查询对象的锁信息_java 怎么查询对象的锁信息_18java 怎么查询对象的锁信息_多线程_20,如果两个线程分别执行两个方法,但此时中断这两个线程,lock不会抛出异常,而lockInterruptibly会抛出异常。

温故知新,未央书斋