Java中各种锁简要介绍

Java中的各种锁

乐观锁与悲观锁

乐观锁和悲观锁是针对数据发生改变时,线程对于同步资源的态度来区分的。悲观锁认为当数据发生改变时,一定有其他的线程修改数据,所以需要将这个数据锁住,确保数据不会被修改。而乐观锁认为当数据发生改变时没有别的线程来修改数据,所以不加锁,但是会在更新数据的时候进行判断,来确保更新的这条数据是自己期望更新的那个数据,如果是则更新,不是则进行报错或者重试。

乐观锁在Java中是通过CAS算法实现的,CAS即Compare And Swap,能够在没有锁的情况下实现多线程之间的变量同步。具体实现原理是:有三个操作数,期望改变的变量值V,实际的旧变量值A和改变后的新变量值B,如果V==A,才进行更新。整个操作时借助的CPU指令完成,具有原子性。

CAS存在的三大问题:

1.ABA问题,即A变为了B又变回了A,在CAS操作中会认为没有发生变化,但实际上发生了两次变化。解决方法是加上一个版本号,即1A->2B->3A。

2.开销大,CAS操作不成功会默认一直尝试更新,会带来大的CPU开销。

3.CAS只能保证对一个共享变量有原子性操作,对于多个共享变量操作时不具备原子性。

自旋锁和适应性自旋锁

当一个线程尝试获取同步资源的锁失败的时候,非自旋锁会进行休眠,CPU则进行切换状态,切换线程等操作,这个操作需要耗费处理器的时间。如果阻塞的时间很短,同步代码块中的操作很简单,那么切换CPU状态就会比较亏。所以为了避免CPU进行状态的切换,可以让没有获取到锁的线程进行自旋,在自旋的过程中不释放CPU资源,等待锁释放。

自旋等待虽然可以避免线程切换的开销,但是如果同步代码块中的操作很多,本身就需要等待很长时间,那么进行自旋就会浪费CPU资源。为了避免这种情况,就需要规定自旋的次数,例如10次没有获取锁就进入休眠。

自旋锁的实现原理也是CAS,在JDK6中自旋锁为默认开启,并且引入了自适应的自旋锁。

适应性自旋锁是指根据这个线程之前自旋获取锁的过程来调整自旋的时间。如果之间获取锁的成功率高则会等待更长的时间,反之如果很少成功则可能直接阻塞线程,避免浪费处理器资源。

无锁、偏向锁、轻量级锁和重量级锁

这四种锁分别是synchronized锁的四种状态。

synchronized锁是通过Monitor(Java对象拥有的内部锁)来实现线程同步的,而Monitor依赖的是底层操作系统的互斥锁。

直接依赖互斥锁实现的锁就会通过切换CPU状态进行同步,这是JDK6之前的synchronized实现方式,效率低,被称之为重量级锁。

无锁则是不对资源进行锁定,所有的线程都能访问并尝试修改同步资源,但是只有一个线程能够成功,其他线程会不断重试直至成功。CAS原理及应用就是无锁的实现。

偏向锁是在没有多线程竞争的情况下,经常访问同步代码块的那一个线程没有必要重复通过CAS加锁和解锁,因此在线程中加上偏向锁,只需要在置换线程ID的时候依赖一次CAS原子指令,避免了资源浪费。当有其他线程尝试竞争偏向锁的时候,持有偏向锁的线程才会释放锁,恢复到无锁或轻量级锁的状态。

轻量级锁就是当持有偏向锁的线程被其他线程访问,偏向锁就会释放变为轻量级锁,其他线程通过自旋的方式获取锁,而不是进行阻塞,以提高性能。

公平锁和非公平锁

公平锁和非公平锁的区分在于获取锁的顺序,如果线程获取锁的顺序是按照先来后到的排队顺序,则是公平锁,反之如果是无序的情况,每个线程同时去争抢资源,就是非公平锁。

公平锁情况下每个线程都会获取到资源,但是除了队列第一个线程外所有线程都会阻塞,CPU唤醒阻塞线程的开销大。而非公平锁的情况下如果线程释放锁的时候恰好有线程来“插队”,则CPU不会执行唤醒等待线程的操作,而是直接让该线程获取到锁,节省了唤醒线程这一步骤的开销,但是可能存在一直有线程无法获取到锁的情况。

可重入锁和非可重入锁

可重入锁又名递归锁,是指在外层方法获取锁后进入内层方法可以自动再次获得锁,不会因为已经获取了锁而导致阻塞。synchronized和ReentrantLock两种锁都是可重入锁。

具体来说,底层有一个同步状态status来计数重入次数,status初始值为0,在线程尝试获取锁的时候,可重入锁判断status==0这个条件,如果等于0则说明当前没有其他线程在执行同步代码,则把status值置为1;如果不为0则判断当前线程是否是获取到这个锁的线程,如果是的话执行status+1,且当前线程可以再次获取锁。而非可重入锁就是直接获取锁并更新当前status值,如果status!=0获取锁失败就进行阻塞。释放锁也一样,可重入锁会先获取当前status的值,在当前线程时持有锁的线程的前提下,如果status值减去1为0,说明其他获取的锁已经释放,则线程才真正释放锁。而非可重入锁则直接将status置为0释放锁。

独享锁和共享锁

独享锁又名排他锁,一次只能被一个线程所持有,用于写数据。共享锁一次可以被多个线程同时获取,但是只能用于读取数据而无法进行修改,一个线程获取共享锁后可以继续获得共享锁,但是无法获取排他锁。

注意:公平锁和非公平锁都是独享锁。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值