JUC包下各种锁使用详解

 1. Java锁的种类

  在笔者面试过程时,经常会被问到各种各样的锁,如乐观锁、读写锁等等,非常繁多,在此做一个总结。介绍的内容如下:

  • 乐观锁/悲观锁
  • 独享锁/共享锁
  • 互斥锁/读写锁
  • 可重入锁/不可重入锁
  • 公平锁/非公平锁
  • 分段锁
  • 偏向锁/轻量级锁/重量级锁
  • 自旋锁

2.锁的分类

 Java设计者按照不同的维度为我们提供了各种锁,锁的分类按照不同的特征分为以下几种:

2.1按上锁方式划分

2.1.1隐式锁:synchronized

synchronized为Java的关键字,是Java提供的同步机制,当它用来修饰一个方法或一个代码块时,能够保证在同一时刻最多只能有一个线程执行该代码。当使用synchronized修饰代码时,并不需要显式的执行加锁和解锁过程,所以它也被称之为隐式锁。

2.1.2显式锁:JUC包中提供的锁

JUC中提供的锁都提供了常用的锁操作,加锁和解锁的方法都是显式的,我们称他们为显式锁。

JUC包中的锁,包括:Lock接口,ReadWriteLock接口,LockSupport阻塞原语,Condition条AbstractOwnableSynchronizer/AbstractQueuedSynchronizer/

AbstractQueuedLongSynchronizer三个抽象类,ReentrantLock独占锁,ReentrantReadWriteLock读写锁。由CountDownLatch,CyclicBarrier和Semaphore也是通过AQS来实现的;因此,我也将它们归纳到锁的框架中进行介绍。 

JUC 三 锁_juc 中三把锁-CSDN博客

2.2按特性划分

2.2按照线程在使用共享资源时,要不要锁住同步资源,划分为:乐观锁和悲观锁。

悲观锁:比较悲观,总是假设最坏的情况,对于同一个数据的并发操作,悲观锁认为自己在使用数据的时候一定有别的线程来修改数据,因此在获取数据的时候会先加锁,确保数据不会被别的线程修改。
实现:JUC的锁、Synchronized

乐观锁:比较乐观,总是假设最好的情况,对于同一个数据的并发操作,乐观锁认为自己在使用数据时不会有别的线程修改数据,所以在获取数据的时候不会添加锁。只有在更新数据的时候才会去判断有没有别的线程更新了这个数据,如果这个数据没有被更新,当前线程将自己修改的数据成功写入;如果数据已经被其他线程更新,则会根据不同的情况执行不同的操作(例如:报错或自动重试)
实现:CAS算法,关系型数据库的版本号机制

2.3按照同一个线程是否可以重复获取同一把锁,划分为:可重入锁和不可重入锁。

重入锁:一个线程可以重复获取同一把锁,不会因为之前已经获取了该锁未释放而被阻塞。在获得一个锁之后未释放锁之前,再次获得同一把锁时,只会增加获得锁的次数,当释放锁时,会同时减少锁定次数。可重入锁的一个优点是可一定程度避免死锁。
实现:ReentrantLock、synchronized

非重入锁:不可重入锁,与可重入锁相反,同一线程获得锁之后不可再次获取,重复获取会发生死锁。

2.4按照多个线程竞争同一锁时需不需要排队,能不能插队,划分为公平锁和非公平锁。 

公平锁:多个线程按照申请锁的顺序来获得锁
实现:new ReentrantLock(true)

非公平锁:多个线程获取锁的顺序并不是按照申请锁的顺序,允许“插队”,有可能后申请的线程比先申请的线程优先获取锁
实现:new ReentrantLock(false),synchronized

2.5按照多个线程能不能同时共享同一个锁,锁被划分为独享锁和排他锁。 

独享锁也叫排他锁,是指同一个锁同时只能被一个线程所持有。如果线程A对获得了锁S后,则其他线程只能阻塞等待线程A释放锁S后,才能获得锁S。
实现:synchronized,ReentrantLock共享锁(读锁)ReentrantReadWriteLock(写锁)

共享锁:多个线程共享
实现:ReentrantReadWriteLock的读锁

2.6其它分类

2.6.1自旋锁

获取锁失败时,线程不会阻塞而是循环尝试获得锁,直至获得锁成功。

实现:CAS,举例:AtomicInteger#getAndIncrement()

 2.6.2分段锁

在并发程序中,使用独占锁时保护共享资源的时候,基本上是采用串行方式,每次只能有一个线程能访问它。串行操作是会降低可伸缩性,在某些情况下我们可以将锁按照某种机制分解为一组独立对象上的锁,这成为分段锁。
说的简单一点:容器里有多把锁,每一把锁用于锁容器其中一部分数据,那么当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率。ConcurrentHashMap所使用的锁分段技术,首先将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。
实现:ConcurrentHashMap 

2.6.3无锁/偏向锁/轻量级锁/重量级锁

锁主要存在四种状态:“无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态”。

偏向锁是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。

轻量级锁是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。

重量级锁是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让他申请的线程进入阻塞,性能降低。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值