暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Java“锁”事

金融科技小站 2020-07-05
407

面试中都离不开java锁,本文巩固一下主流锁的基本概念~后续再深入源码~



Java提供了种类丰富的锁:悲观锁、乐观锁、可重入锁、非可重入锁、自旋锁、公平锁、非公平锁、无锁、共享锁、拍他锁、偏向锁、重量级锁、轻量级锁


当面试官闻到:聊聊你对锁的理解~该如何回答呢?


首先分类:


再者对比:

一:悲观锁 vs 乐观锁

这两种锁是一种广义上的定义,首先来说概念:

悲观锁:对于同一个资源进行多线程操作的时候,悲观锁认为它操作的数据会被其他线程来修改数据,因此在获取数据时就加锁确保其他线程不能修改数据。

synchronized关键字和Lock的实现类都是悲观锁。



乐观锁在获取数据的时候不会加锁,只是修改的时候判断自己拿到的数据是否被修改,没有修改则更新,如果有修改则根据不同的实现有不同的操作。

java.util.concurrent包中的原子类都是乐观锁,最常采用的是CAS算法


悲观锁:适合写操作多的场景,先加锁可以保证写操作时数据正确。

乐观锁:适合读操作多的场景,不加锁的特点能够使其读操作的性能大幅提升。



二:公平锁 vs 非公平锁

公平锁:是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。

优点:是等待锁的线程不会饿死。按顺序获取锁

缺点:是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。


非公平锁:是多个线程加锁时直接尝试获取锁,获取不到才会到等待队列的队尾等待。

优点:可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。

缺点:处于等待队列中的线程可能会饿死,或者等很久才会获得锁



三:可重入锁 vs 非可重入锁

可重入锁:是指在同一个线程在外层方法获取锁的时候,再进入该线程的子方法会自动获取锁。可一定程度避免死锁,不会因为之前已经获取过还没释放而阻塞

Java中ReentrantLock和synchronized都是可重入锁




不可重入锁:相对于可重入锁而言,当前线程在调用子方法时需要释放当前锁,实际上该对象锁已被当前线程所持有,且无法释放。所以此时会出现死锁


例如:执行方法a时,a方法获取之后,需要调用子方法b,但是此时该对象的对象锁已经被方法a拿走了,子方法b得不到需要的对象锁,只能等待方法a将对象锁释放,而a方法又必须调用完子方法b才能释放对象锁。



四:共享锁 vs 排他锁

共享锁:指该锁可被多个线程所持有

排他锁:是指该锁一次只能被一个线程所持有。

synchronized和JUC中Lock的实现类就是排他锁。


五:自旋锁

众所周知:阻塞或唤醒一个Java线程需要操作系统切换CPU状态来完成,这种状态转换需要耗费处理器时间

自旋锁请求获取锁的线程自旋不释放cpu时间,如果在自旋完成后前面锁定同步资源的线程已经释放了锁,那么当前线程就可以不必阻塞而是直接获取同步资源,从而避免切换线程的开销。


六:偏向锁、重量级锁、轻量级锁、无锁

这四种锁是指锁的状态,专门针对synchronized的。

偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁,降低获取锁的代价。
轻量级锁:被另外的线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,从而提高性能。
重量级锁:轻量级锁升级为重量级锁时,锁标志的状态值变为“10”,Synchronized可以把任意一个非NULL的对象当作锁。


状态存储内容优点
缺点
偏向锁偏向线程ID、偏向时间戳、对象分代年龄、是否是偏向锁(1)加锁、释放锁不需要额外的消耗
线程间存在锁竞争时,会带来额外的锁撤销的消耗
轻量级锁指向栈中锁记录的指针竞争线程不会阻塞
使用自旋消耗cpu
重量级锁
指向互斥量(重量级锁)的指针线程竞争不使用自旋,不消耗cpu
线程阻塞,响应时间慢


结语

  • 限于篇幅、时间以及个人水平,没有对锁的内容进行深层次的讲解。

  • 只是对常用的锁以及常见的锁的概念进行了基本介绍。

  • 有时间从源码以及实际应用的角度进行了对比分析。


开篇所提到的面试题文章来自简书~


文章转载自金融科技小站,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论