并发编程--锁
Posted liaowenhui
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了并发编程--锁相关的知识,希望对你有一定的参考价值。
谈谈你对锁的理解?
在并发编程中有两个重要的概念:线程和锁
多线程是一把双刃剑,它在提高程序性能的同时,也带来了编码的复杂性。
锁的出现就是为了保障多线程在同时操作一组资源时的数据一致性,当我们给资源加上锁之后,只有拥有此锁的线程才能操作此资源,而其他线程只能排队等待使用此锁。
锁的分类
乐观锁和悲观锁,公平锁和非公平锁,独占锁和共享锁,可重入锁,自旋锁
乐观锁和悲观锁
悲观锁和乐观锁并不是某个具体的“锁”而是一种并发编程的基本概念,是根据看待并发同步的角度。乐观锁和悲观锁最早出现在数据库的设计当中,后来逐渐被 Java 的并发包所引入。
悲观锁
悲观锁认为对于同一个数据的并发操作一定是会发生修改的,采取加锁的形式,悲观地认为,不加锁的并发操作一定会出问题。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中Synchronized和ReentrantLock等独占锁就是悲观锁思想实现的。
乐观锁
乐观锁正好和悲观锁相反,它获取数据的时候,并不担心数据被修改,每次获取数据的时候也不会加锁,只是在更新数据的时候,通过判断现有的数据是否和原数据一致来判断数据是否被其他线程操作,如果没被其他线程修改则进行数据更新,如果被其他线程修改则不进行数据更新。Lock 是乐观锁的典型实现案例。
补充:更详细的介绍请查看我的另一篇博文
理解悲观锁和乐观锁及其实现方式 -- https://www.cnblogs.com/liaowenhui/p/12524485.html
公平锁和非公平锁
根据线程获取锁的抢占机制,锁又可以分为公平锁和非公平锁。
公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁。
非公平锁
非公平锁是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请的线程比先申请的线程优先获取锁(允许“插队”的情况存在)。
举例说明:
ReentrantLock ,可通过构造函数设置一个 boolean 类型的值,来决定选择公平锁和非公平锁的实现。
公平锁:new ReentrantLock(true)
非公平锁:new ReentrantLock(false)
而公平锁由于有挂起和恢复所以存在一定的开销,因此性能不如非公平锁,所以构造函数不传任何参数的时候,默认提供的是非公平锁。
独占锁和共享锁
根据锁能否被多个线程持有,可以把锁分为独占锁和共享锁。
独占锁
独占锁是指任何时候都只能有一个线程能执行资源操作(只能被单线程持有的锁)。比如 synchronized 就是独占锁。
共享锁
共享锁指定是可以同时被多个线程读取,但只能被一个线程修改。比如 Java 中的 ReentrantReadWriteLock和ReadWriteLock就是共享锁的实现方式,它允许一个线程进行写操作,允许多个线程读操作。
可重入锁
可重入锁也叫递归锁,指的是同一个线程,如果外面的函数拥有此锁之后,内层的函数也可以继续获取该锁。在 Java 语言中 ReentrantLock 和 synchronized 都是可重入锁。
下面我们用 synchronized 来演示一下什么是可重入锁,代码如下:
/** * @author 佛大java程序员 * @since 1.0.0 */ public class LockExample { public static void main(String[] args) { //可重入锁A reentrantA(); } /** * 可重入锁A方法 */ private synchronized static void reentrantA(){ System.out.println(Thread.currentThread().getName() + ":执行 reentrantA"); reentrantB(); } /** * 可重入锁B方法 */ private synchronized static void reentrantB(){ System.out.println(Thread.currentThread().getName() + ":执行 reentrantB"); } }
运行结果:
从结果可以看出reentrantA方法和reentrantB方法的执行线程都是“main”,我们调用了reentrantA方法,它的方法中嵌套了reentrantB,如果 synchronized 是不可重入的话,那么线程会被一直堵塞。
可重入锁的实现原理,是在锁内部存储了一个线程标识,用于判断当前的锁属于哪个线程,并且锁的内部维护了一个计数器,当锁空闲时此计数器的值为0,当被线程占用和重入时分别加1,当锁被释放时计数器减1,直到减到 0 时表示此锁为空闲状态。
自旋锁
自旋锁是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁,这样的好处是减少线程上下文切换的消耗,缺点是循环会消耗 CPU。
常见面试题
(1)谈谈你对锁的理解?
参考
拉钩课程 -- java面试真题及源码
https://kaiwu.lagou.com/course/courseInfo.htm?courseId=59#/detail/pc?id=1766
以上是关于并发编程--锁的主要内容,如果未能解决你的问题,请参考以下文章