天天看点

9.互联网大厂高频面试题-锁

文章目录

  • ​​公平和非公平锁​​
  • ​​是什么​​
  • ​​两者区别​​
  • ​​题外话sync与reentrantlock​​
  • ​​可重入锁和递归锁理论知识​​
  • ​​是什么​​
  • ​​可重入锁和递归锁代码验证​​
  • ​​自旋锁理论知识​​
  • ​​自旋锁代码验证-手写一个自旋锁​​
  • ​​读写锁理论知识​​
  • ​​读写锁代码验证​​
  • ​​CountdownLatch​​
  • ​​枚举优化countdownlatch​​
  • ​​CyclicBarrier​​
  • ​​semaphore​​

公平和非公平锁

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

是什么

Reentrantlock的构造方法如下:

9.互联网大厂高频面试题-锁

空构造底层是非公平锁。相当于Reentrantlock(false)。

可以传入bool类型的参数,来改变锁的类型,是公平还是非公平。

9.互联网大厂高频面试题-锁

公平和非公平,指的是排序策略。

9.互联网大厂高频面试题-锁

公平锁:就是有个队列,执行讲究先来后到。

非公平锁:就是允许有后来的线程加塞执行。

9.互联网大厂高频面试题-锁

优先级反转:后来的先执行。

饥饿现象:排在最后的总是得不到执行。

两者区别

9.互联网大厂高频面试题-锁

题外话sync与reentrantlock

9.互联网大厂高频面试题-锁

可重入锁和递归锁理论知识

9.互联网大厂高频面试题-锁

reentrantlock:字面意思就是可重入锁。可重入锁就是递归锁。

9.互联网大厂高频面试题-锁

是什么

9.互联网大厂高频面试题-锁

进入家里门之后,再进厨房是不需要开锁的,跟这个一个道理。

9.互联网大厂高频面试题-锁

这俩是典型的可重入锁(非公平)。最大作用是避免死锁。

9.互联网大厂高频面试题-锁

可重入锁和递归锁代码验证

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

测试代码:

9.互联网大厂高频面试题-锁

结果:

9.互联网大厂高频面试题-锁

现象是同步方法可以进入同步方法。

案例二:证明reentrantlock也是重入锁。

9.互联网大厂高频面试题-锁

代码补足:

9.互联网大厂高频面试题-锁

测试代码+执行结果:

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

这个实验证明,reentrantlock也是重入锁。

类似的场景还有子类调用父类的,也可以证明。

深化理解:如果加两个锁呢?

9.互联网大厂高频面试题-锁

首先,编译器没报错,语法是没有问题的。

运行,也是没有问题的。

9.互联网大厂高频面试题-锁

说明只要你的锁匹配上有lock有unlock,几把都可以的。

但是如果缺一个unlock,没有全部配对上,语法是没问题的。但是执行就不行了,没有解锁!

9.互联网大厂高频面试题-锁

此时程序卡死了!所以一定要加锁几次,就解锁几次!

理论+代码+总结。学习三板斧,受教了!

自旋锁理论知识

之前讲过的自旋锁的相关,atomic类是用的unsafe类+cas思想(自旋)。

9.互联网大厂高频面试题-锁

生活中的case:自旋的反义词是阻塞。我在打电话,有人来问我问题,他就站我身边或者看到堵塞去忙自己的事 这两种选择。忙一会回来看看,忙一会回来看这种行为,就可以理解为自旋。这是老师的例子,我觉得不好。应该是频繁重新尝试获得锁,就像你在打电话,然后我不断的问:打完了吗?打完了吗?尝试着我想做的事情。

自旋锁代码验证-手写一个自旋锁

9.互联网大厂高频面试题-锁

对引用类型的原子类引用,啥都不写,指向的就是null。

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

通过cas的思想,实现加锁与解锁的方法,当有第一个线程访问的时候,mylock的while判定是false(因为对引用类型的原子类引用,啥都不写,指向的就是null,所以compareAndSet的结果就是true,取反为false),就没有进入循环,相当于获得了锁,解锁的逻辑是用完了,把当前线程更新为null,可以被其他线程访问了。如果过程中有其他线程进入访问,就会进入while循环,从而不断尝试获得锁,而不是做线程切换。

测试代码:

9.互联网大厂高频面试题-锁

运行效果:

9.互联网大厂高频面试题-锁

BB通过在线程里自旋,完成多次尝试获得锁,而不是阻塞。可能会消耗性能。

读写锁理论知识

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

独占锁,也叫写锁。共享锁也叫读锁,可以被很多线程并发读。

锁的进化历程:

9.互联网大厂高频面试题-锁

以前的锁,都是独占的,所以进化出了读写分离的读写锁。

很重要的一点:知识点一定要用代码证明!

9.互联网大厂高频面试题-锁

读写锁代码验证

写法一:

9.互联网大厂高频面试题-锁

原子性得到了保障,配合volatile,满足jmm模型的三大特性要求,但是读取的时候也独占就效率太低了。

题外话:缓存的三大接口:添加,读取,清空。

模拟一个不加锁的场景:

9.互联网大厂高频面试题-锁

对于写操作,满足原子+独占,整个过程必须是一个完整的统一体,中间不许被分割,被打断。

9.互联网大厂高频面试题-锁

测试代码:5个线程读,5个线程写,先写入再读取。执行结果:

9.互联网大厂高频面试题-锁

会发现写入操作,中间被打断了很多次。这种情况传统锁,可以满足独占,但是业务场景需要同读同写,这样传统的锁就满足不了需求了,性能太差了。

加入读写锁:

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

运行结果:

9.互联网大厂高频面试题-锁

效果对比:

9.互联网大厂高频面试题-锁

CountdownLatch

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

作用:火箭发射倒计时

9.互联网大厂高频面试题-锁

api翻译:countdownlatch在初始化的时候会被赋予一个计数,await方法会阻塞线程,直到这个计数减为0(类似火箭发射,燃料正常,逃逸仓正常,电压正常…)。

演示原生代码,遇到这种需要等着前面的子节点操作完了之后再执行操作的场景(类似火箭发射),不做处理:

9.互联网大厂高频面试题-锁

结果是原地爆炸:

9.互联网大厂高频面试题-锁

这种场景的解决方案就是countdownlatch。代码如下:

9.互联网大厂高频面试题-锁

主线程堵塞,在每个线程里面减一,当count变成0之后,就释放锁,然后继续往下执行。

运行结果:

9.互联网大厂高频面试题-锁

如果现在有这种需求,有id,有实际的名字,要求按照id,不做if判断的情况下,输出实际的名字在打印方法中。

上述需求的解决方案就是:枚举

枚举优化countdownlatch

这是一种把枚举当成数据库(k-v型)来用,也可以用redis缓存,但是用缓存会增加打开关闭的开销,也不值当的。这种需要对应的场景,用枚举是很好的选择,可以省去很多if判断。

枚举类的名字就是数据库的名字,然后每个枚举里面的内容就是一条数据。

设计枚举类:

9.互联网大厂高频面试题-锁

根据id返回:

9.互联网大厂高频面试题-锁

业务测试代码改为:

9.互联网大厂高频面试题-锁

测试结果:

9.互联网大厂高频面试题-锁
9.互联网大厂高频面试题-锁

CyclicBarrier

用法:集齐7个龙珠就能召唤神龙。人到齐了才能开会。

9.互联网大厂高频面试题-锁

代码案例:

9.互联网大厂高频面试题-锁

先到的被堵塞。

9.互联网大厂高频面试题-锁

semaphore

作用:抢车位,多个资源抢多个资源。

9.互联网大厂高频面试题-锁

代码演示:6个车三个车位

9.互联网大厂高频面试题-锁

继续阅读