Java并发实现原理:JDK源码解析之synchronized关键字

1.3.1 锁的对象是什么
对不熟悉多线程原理的人来说,很容易误解 synchronized 关键 字:它通常加在所有的静态成员函数和非静态成员函数的前面,表面 看好像是“函数之间的互斥”,其实不是。synchronized关键字其实 是“给某个对象加了把锁”,这个锁究竟加在了什么对象上面?如下面的代码所示,给函数f1()、f2()加上synchronized关键字。
在这里插入图片描述

等价于如下代码:
在这里插入图片描述

对于非静态成员函数,锁其实是加在对象a上面的;对于静态成员函数,锁是加在A.class上面的。当然,class本身也是对象。
这间接回答了关于 synchronized 的常见问题:一个静态成员函 数和一个非静态成员函数,都加了synchronized关键字,分别被两个线程调用,它们是否互斥?很显然,因为是两把不同的锁,所以不会互斥。
1.3.2 锁的本质是什么

无论使用什么编程语言,只要是多线程的,就一定会涉及锁。既然锁如此常见,那么锁的本质到底是什么呢?
如图1-2所示,多个线程要访问同一个资源。线程就是一段段运行 的代码;资源就是一个变量、一个对象或一个文件等;而锁就是要实 现线程对资源的访问控制,保证同一时间只能有一个线程去访问某一 个资源。打个比方,线程就是一个个游客,资源就是一个待参观的房 子。这个房子同一时间只允许一个游客进去参观,当一个人出来后下 一个人才能进去。而锁,就是这个房子门口的守卫。如果同一时间允许多个游客参观,锁就变成信号量,这点后面会专门讨论。
在这里插入图片描述
从程序角度来看,锁其实就是一个“对象”,这个对象要完成以下几件事情:

(1)这个对象内部得有一个标志位(state变量),记录自己有 没有被某个线程占用(也就是记录当前有没有游客已经进入了房 子)。最简单的情况是这个state有0、1两个取值,0表示没有线程占
用这个锁,1表示有某个线程占用了这个锁。

(2)如果这个对象被某个线程占用,它得记录这个线程的thread
ID,知道自己是被哪个线程占用了(也就是记录现在是谁在房子里
面)。

(3)这个对象还得维护一个thread id list,记录其他所有阻塞 的、等待拿这个锁的线程(也就是记录所有在外边等待的游客)。在 当前线程释放锁之后(也就是把state从1改回0),从这个thread idlist里面取一个线程唤醒。
既然锁是一个“对象”,要访问的共享资源本身也是一个对象, 例如前面的对象 a,这两个对象可以合成一个对象。代码就变成synchronized(this){…},我们要访问的共享资源是对象a,锁也是加在 对象a上面的。当然,也可以另外新建一个对象,代码变成synchronized(obj1){…}。这个时候,访问的共享资源是对象a,而锁是加在新建的对象obj1上面的。
资源和锁合二为一,使得在Java里面,synchronized关键字可以 加在任何对象的成员上面。这意味着,这个对象既是共享资源,同时也具备“锁”的功能!
下面来看 Java 是如何做到让任何一个对象都具备“锁”的功能的,这也就是 synchronized的实现原理。
1.3.3 synchronized实现原理 答案在Java的对象头里。
在对象头里,有一块数据叫Mark Word。 在64位机器上,Mark Word是8字节(64位)的,这64位中有2个重要字 段:锁标志位和占用该锁的thread ID。因为不同版本的JVM实现,对
象头的数据结构会有各种差异,此处不再进一步讨论。
此处主要是想说明锁实现的思路,因为后面讲ReentrantLock的详 细实现时,也基于类似的思路。在这个基本的思路之上,synchronized还会有偏向、自旋等优化策略,ReentrantLock同样会用到这些优化策略,到时会结合代码详细展开。

关注公众号:有故事的程序员,领取这份《Java并发实现原理:JDK源码解析》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值