问题

(1)自己动手写一个锁需要哪些知识?

(2)自己动手写一个锁到底有多简单?

(3)自己能不能写出来一个完美的锁?

简介

本篇文章的目标一是自己动手写一个锁,这个锁的功能很简单,能进行正常的加锁、解锁操作。

本篇文章的目标二是通过自己动手写一个锁,能更好地理解后面章节将要学习的AQS及各种同步器实现的原理。

分析

自己动手写一个锁需要准备些什么呢?

首先,在上一章学习synchronized的时候我们说过它的实现原理是更改对象头中的MarkWord,标记为已加锁或未加锁。

但是,我们自己是无法修改对象头信息的,那么我们可不可以用一个变量来代替呢?

比如,这个变量的值为1的时候就说明已加锁,变量值为0的时候就说明未加锁,我觉得可行。

其次,我们要保证多个线程对上面我们定义的变量的争用是可控的,所谓可控即同时只能有一个线程把它的值修改为1,且当它的值为1的时候其它线程不能再修改它的值,这种是不是就是典型的CAS操作,所以我们需要使用Unsafe这个类来做CAS操作。

然后,我们知道在多线程的环境下,多个线程对同一个锁的争用肯定只有一个能成功,那么,其它的线程就要排队,所以我们还需要一个队列。

最后,这些线程排队的时候干嘛呢?它们不能再继续执行自己的程序,那就只能阻塞了,阻塞完了当轮到这个线程的时候还要唤醒,所以我们还需要Unsfae这个类来阻塞(park)和唤醒(unpark)线程。

基于以上四点,我们需要的神器大致有:一个变量、一个队列、执行CAS/park/unpark的Unsafe类。

大概的流程图如下图所示:

关于Unsafe类的相关讲解请参考彤哥之前发的文章:

【死磕 java魔法类之Unsafe解析】

解决

一个变量

这个变量只支持同时只有一个线程能把它修改为1,所以它修改完了一定要让其它线程可见,因此,这个变量需要使用volatile来修饰。

private 

CAS

这个变量的修改必须是原子操作,所以我们需要CAS更新它,我们这里使用Unsafe来直接CAS更新int类型的state。

当然,这个变量如果直接使用AtomicInteger也是可以的,不过,既然我们学习了更底层的Unsafe类那就应该用(浪)起来。

private 

一个队列

队列的实现有很多,数组、链表都可以,我们这里采用链表,毕竟链表实现队列相对简单一些,不用考虑扩容等问题。

这个队列的操作很有特点:

放元素的时候都是放到尾部,且可能是多个线程一起放,所以对尾部的操作要CAS更新;

唤醒一个元素的时候从头部开始,但同时只有一个线程在操作,即获得了锁的那个线程,所以对头部的操作不需要CAS去更新。

private 

这个队列很简单,存储的元素是线程,需要有指向下一个待唤醒的节点,前一个节点可有可无,但是没有实现起来很困难,不信学完这篇文章你试试。

加锁

public 

(1)尝试获取锁,成功了就直接返回;

(2)未获取到锁,就进入队列排队;

(3)入队之后,再次尝试获取锁;

(4)如果不成功,就阻塞;

(5)如果成功了,就把头节点后移一位,并清空当前节点的内容,且与上一个节点断绝关系;

(6)加锁结束;

解锁

// 解锁

(1)把state改成0,这里不需要CAS更新,因为现在还在加锁中,只有一个线程去更新,在这句之后就释放了锁;

(2)如果有下一个节点就唤醒它;

(3)唤醒之后就会接着走上面lock()方法的while循环再去尝试获取锁;

(4)唤醒的线程不是百分之百能获取到锁的,因为这里state更新成0的时候就解锁了,之后可能就有线程去尝试加锁了。

测试

上面完整的锁的实现就完了,是不是很简单,但是它是不是真的可靠呢,敢不敢来试试?!

直接上测试代码:

private 

运行这段代码的结果是总是打印出10000000(一千万),说明我们的锁是正确的、可靠的、完美的。

总结

(1)自己动手写一个锁需要做准备:一个变量、一个队列、Unsafe类。

(2)原子更新变量为1说明获得锁成功;

(3)原子更新变量为1失败说明获得锁失败,进入队列排队;

(4)更新队列尾节点的时候是多线程竞争的,所以要使用原子更新;

(5)更新队列头节点的时候只有一个线程,不存在竞争,所以不需要使用原子更新;

(6)队列节点中的前一个节点prev的使用很巧妙,没有它将很难实现一个锁,只有写过的人才明白,不信你试试^^

彩蛋

(1)我们实现的锁支持可重入吗?

答:不可重入,因为我们每次只把state更新为1。如果要支持可重入也很简单,获取锁时检测当前锁是不是当前线程占有着,如果是就把state的值加1,释放锁时每次减1即可,减为0时表示锁已释放。

(2)我们实现的锁是公平锁还是非公平锁?

答:非公平锁,因为获取锁的时候我们先尝试了一次,这里并不是严格的排队,所以是非公平锁。

注:下一章我们将开始分析传说中的AQS,这章是基础,请各位老铁务必搞明白。

推荐阅读

  1. 死磕 java魔法类之Unsafe解析
  2. 死磕 java同步系列之JMM(Java Memory Model)
  3. 死磕 java同步系列之volatile解析
  4. 死磕 java同步系列之synchronized解析

欢迎关注我的公众号“彤哥读源码”,查看更多源码系列文章, 与彤哥一起畅游源码的海洋。

java 同步锁_死磕 java同步系列之自己动手写一个锁Lock相关推荐

  1. java任务流程_死磕 java线程系列之线程池深入解析——普通任务执行流程

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 前面我们一起学习了Java中 ...

  2. 2019死磕java面试题_死磕 java同步系列之开篇

    简介 同步系列,这是彤哥想了好久的名字,本来是准备写锁相关的内容,但是java中的CountDownLatch.Semaphore.CyclicBarrier这些类又不属于锁,它们和锁又有很多共同点, ...

  3. java 手编线程池_死磕 java线程系列之自己动手写一个线程池

    欢迎关注我的公众号"彤哥读源码",查看更多源码系列文章, 与彤哥一起畅游源码的海洋. (手机横屏看源码更方便) 问题 (1)自己动手写一个线程池需要考虑哪些因素? (2)自己动手写 ...

  4. java ee是什么_死磕 java集合之HashSet源码分析

    问题 (1)集合(Collection)和集合(Set)有什么区别? (2)HashSet怎么保证添加元素不重复? (3)HashSet是否允许null元素? (4)HashSet是有序的吗? (5) ...

  5. java unsafe 详解_死磕 java魔法类之Unsafe解析

    问题 (1)Unsafe是什么? (2)Unsafe具有哪些功能? (3)Unsafe为什么是不安全的? (4)怎么使用Unsafe? 简介 本章是java并发包专题的第一章,但是第一篇写的却不是ja ...

  6. java 原子类能做什么_死磕 java原子类之终结篇(面试题)

    概览 原子操作是指不会被线程调度机制打断的操作,这种操作一旦开始,就一直运行到结束,中间不会有任何线程上下文切换. 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割 ...

  7. java线程池深入讲解_死磕 java线程系列之线程池深入解析——生命周期

    (手机横屏看源码更方便) 注:java源码分析部分如无特殊说明均基于 java8 版本. 注:线程池源码部分如无特殊说明均指ThreadPoolExecutor类. 简介 上一章我们一起重温了下线程的 ...

  8. java方法和变量修饰符有哪些_死磕Java基础---类,变量和方法的修饰符

    欢迎关注微信公众号:一个自学的程序员 类修饰符 对于类的修饰符,毫无疑问是用来修饰类的,那么,修饰类的修饰符都有哪些? 有如下这些: 1. abstract 2. final 3. private 4 ...

  9. hashmap修改对应key的值_死磕 java集合之HashMap源码分析

    简介 HashMap采用key/value存储结构,每个key对应唯一的value,查询和修改的速度都很快,能达到O(1)的平均时间复杂度.它是非线程安全的,且不保证元素存储的顺序: 继承体系 Has ...

最新文章

  1. Redis实现广告缓存、并完善缓存击穿
  2. VGA12h与VGA寄存器
  3. [YTU]_1046 ( 输入10个数字,然后逆序输出)
  4. 社会化图标html,[CSS]响应式社会化分享按钮
  5. 论文笔记——Deep Model Compression Distilling Knowledge from Noisy Teachers
  6. 恒强制版系统980_速来围观 | 恒强制版小图高级功能讲解
  7. python编辑器_资深程序员:学Python我推荐你用这几款编辑器
  8. Helios Service Release 2安装SVN
  9. 19日下午三点直播:DevOps体系中数据库端的四大问题及解决之道
  10. 常微分方程的初始条件使用
  11. 策略模式 (Strategy)
  12. C语言-用指针实现内存动态分配
  13. Jmeter安装步骤
  14. Intellij IDear关闭页面浏览器显示图标
  15. rsync 是什么?
  16. jasypt 配置文件加解密
  17. Crazy Number---3755
  18. 用计算机写文章 单元备课,备课写教案
  19. 主动笔驱动芯片市场现状及未来发展趋势
  20. Vue前端Es6语法Object.assign()

热门文章

  1. Java9都快发布了,Java8的十大新特性你了解多少呢?
  2. Ant步步为营(4)ant启动tomcat
  3. 一个完美网站的101项指标.第一部分.概述
  4. 4.1-大秦立国-ip演变
  5. Hadoop相关技术
  6. 第二章 数据的表示和运算 2.1.6 循环冗余校验码/CRC码 [计算机组成原理笔记]
  7. Leetcode--542. 01 矩阵(java)
  8. 【剑指offer】面试题35:复杂链表的复制(Java 实现)
  9. php jquery 源码,最新版jQuery 2.1.0完整
  10. linux vim debugger,Vim 调试:termdebug 入门