原始自旋锁最原始的自旋锁就是多个线程不断自旋,大家都不断尝试获取锁 。看下面例子,主要看lock和unlock两个方法,Unsafe仅仅是为操作提供了硬件级别的原子CAS操作 。对于lock方法,假如有若干线程竞争,能成功通过CAS将value值修改为newV的线程即是成功获取锁的线程 。成功获取锁的线程将顺利通过,而其它线程则不断在循环检测value值是否改回0,将value改为0的操作就是获取锁的线程释放锁的操作 。对于unlock方法,用于释放锁,释放后其它线程又继续对该锁竞争 。如此一来,没获得锁的线程也不会被挂起或阻塞,而是不断循环检查状态 。
1. public class SpinLock {
2. private static Unsafe unsafe = null;
3. private static final long valueOffset;
4. private volatile int value = https://www.isolves.com/it/cxkf/bk/2020-08-20/0;
5. static {
6. try {
7. unsafe = getUnsafeInstance();
8. valueOffset = unsafe.objectFieldOffset(SpinLock.class.getDeclaredField("value"));
9. } catch (Exception ex) {
10. throw new Error(ex);
11. }
12. }
13.
14. private static Unsafe getUnsafeInstance() throws Exception {
15. Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
16. theUnsafeInstance.setAccessible(true);
17. return (Unsafe) theUnsafeInstance.get(Unsafe.class);
18. }
19.
20. public void lock() {
21. for (;;) {
22. int newV = value + 1;
23. if (newV == 1)
24. if (unsafe.compareAndSwapInt(this, valueOffset, 0, newV)) {
25. return;
26. }
27. }
28. }
29.
30. public void unlock() {
31. unsafe.compareAndSwapInt(this, valueOffset, 1, 0);
32. }
33. }
排队自旋锁鉴于原始自旋锁存在公平性问题,于是引入一种排队机制来解决它,这就是排队自旋锁 。所有线程在尝试获取锁之前得先拿到一个排队号,然后再不断轮询当前是不是已经轮到自己了,判断的依据就是当前处理号是否等于自己的排队号 。如果两者相等,则表示已经轮到自己了,于是得到锁并往下执行 。
看下面例子,主要看lock和unlock两个方法,Unsafe仅仅是为操作提供了硬件级别的原子CAS操作 。对于lock方法,首先通过不断循环去尝试拿到一个排队号,一旦成功拿到排队号后就开始通过while(processingNum != nowNum)轮询看自己是否已经轮到了 。而unlock方法则是直接修改当前处理号,直接加1,表示自己已经不需要锁了,可以让给下一位了 。
1. public class TicketLock {
2. private static Unsafe unsafe = null;
3. private static final long ticketNumOffset;
4. private static final long processingNumOffset;
5. private volatile int ticketNum = 0;
6. private volatile int processingNum = 0;
7. static {
8. try {
9. unsafe = getUnsafeInstance();
10. ticketNumOffset = unsafe
11. .objectFieldOffset(TicketLock.class.getDeclaredField("ticketNum"));
12. processingNumOffset = unsafe
13. .objectFieldOffset(TicketLock.class.getDeclaredField("processingNum"));
14. } catch (Exception ex) {
15. throw new Error(ex);
16. }
17. }
18.
19. private static Unsafe getUnsafeInstance() throws Exception {
20. Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");
21. theUnsafeInstance.setAccessible(true);
22. return (Unsafe) theUnsafeInstance.get(Unsafe.class);
23. }
24.
25. public int lock() {
26. int nowNum;
27. for (;;) {
28. nowNum = ticketNum;
29. if (unsafe.compareAndSwapInt(this, ticketNumOffset, ticketNum, ticketNum + 1)) {
30. break;
31. }
32. }
33. while (processingNum != nowNum) {
34. }
35.
36. return nowNum;
37. }
38.
39. public void unlock(int ticket) {
40. int next = ticket + 1;
41. unsafe.compareAndSwapInt(this, processingNumOffset, ticket, next);
42. }
【自己动手实现四种自旋锁】43.
44. }
CLH锁为了优化同步带来的花销,Craig、Landin、Hagersten三个人发明了CLH锁 。其核心思想是:通过一定手段将所有线程对某一共享变量的轮询竞争转化为一个线程队列,且队列中的线程各自轮询自己的本地变量 。
推荐阅读
- 5种python方法实现冒泡排序可视化:Bubble Sort Visualizer
- 你们要的MyCat实现MySQL分库分表来了
- 宕机后,Redis如何实现快速恢复?
- 网址太长?轻松搭建自己的短网址平台Shortny v2.0.1源码搭建教程
- 一文搞定 Koa 中间件实现原理
- 如何有效提臀呢?
- 怎样自己在家练瑜伽呢
- 男性自己可以练瑜伽吗
- 自己在家练瑜伽可以吗
- 如何快速增强肌肉力量?