Java多线程闲聊(六):synchronized关键字

Java多线程闲聊(六):synchronized关键字


前言

这篇文章我会在博客置顶,为什么呢?因为,三篇引用的文章写得太好了,我害怕后面找不到,看不到,然后忘了!

让我想想,感觉昨天的前言把最近肚子里的墨水都说完了。所以,今天想在前言里写一些什么有一种肠道炎的感觉,斟酌再三,决定就不写啦!

还是写写吧,很多时候,我写博客,仅仅是为了记录我自己的成长过程,如果哪里写的有问题,那正说明我还有很大的进步空间,这的确是一件好事。我之前撤过两篇文章,每篇都有几千字,为什么撤掉了呢?因为别人看到了,向我提出了疑问,然后我发现,那时候的我的确不够成熟,对于知识的理解还有很大的偏差,不能让自己的文章误导别人,所以还是撤掉吧,虽然特地花了时间去写。


正文

synchronized关键字可以说是Java多线程最常见的关键字了,然而在新的版本之中却越来越少见了,这是为什么呢?

对于synchronized关键字,Java其实对其进行了一系列的优化,也因此该关键字有很多种状态,这些状态是什么意思呢?他们之间有什么联系呢?

在这篇文章里,我还是希望自己能够搞清楚这些问题以及一些细节。不禁感慨,真的是随着自己学习地越深入,特别是稍微看了一些稍微深入的博客(链接已经附在文章末尾),就越发认为自己的未知信息就越多啊。为了不让文章的篇幅过于冗长,这里先简单讲讲synchronized关键字的一些基本逻辑。我感觉这个系列绝对写不完了,多线程这块的内容真的是挖不完啊,就连今天写的内容,后面绝对还需要回来重新的,进一步挖掘!

只要能传达信息,那就是语言,只是高效与否,准确与否的差异而已。

Java中的synchronized的作用其实就是为一个对象加锁,注意,这里没有表达错误,是对象,既不是实例,也不是类。Java语言的核心思想之一就是万物皆为对象,就算是类也一样,因为类在JVM内,也是对象,class对象。

一旦某个线程通过syn关键字拿到了锁,那么当其他的线程尝试拿同一把锁时(注意,是用一把锁,不同的锁你随便拿,实例锁和类锁并不是同一把锁),就会陷入阻塞。一旦线程陷入阻塞了,就意味着他会挂起从而让出CPU资源,直到线程释放了锁,他才会被唤醒,进去到抢夺的行列。
Java多线程闲聊(六):synchronized关键字

但是一旦将线程挂起,那么就意味着进行上下文切换的工作,从而将CPU资源让给别人。毕竟在最初的设计中,鬼知道什么时候这个线程什么时候才能有机会去抢锁呢?直到拿了锁的线程释放了锁,他将会从条件队列被取出,尝试去拿一次锁,如果拿到了,那么就可以执行自己的任务了。但是如果没拿到,抱歉啦,你们这些没有拿到锁的同学就到同步队列排队吧,这就是非公平锁。

为什么是非公平呢?因为在条件队列里面我们明明都按照先来后到的顺序排好队了,为什么大家都有机会一拥而上去抢一次锁呢?

好了,线程按照顺序进入了同步队列啦,然后我们怎么处理这个线程呢?最初,是采用一种CLH同步队列的方式,可能也是因为量级比较小的原因了,我们将每个线程包装成一个节点(包含了前一个节点的引用,以及当前节点的获得锁状态,也因此被称为队列)并采用一种类似CAS的自旋方式来不断进行判断,看看前面一个节点是不是为空或者前一个节点有没有释放锁。如果前一个节点为空或者locked状态为false,那就说明当前节点拥有了资格去获得锁来执行自己的任务。

但是很明显,CLH这样的机制存在着一些问题,第一,如果拿到锁的线程在执行一个很耗费时间的任务,那么没有拿到锁的线程就会不断地去进行自旋希望能获取到锁,这无疑是对于CPU资源的一种浪费;第二,单向链表的数据结构,在移除中间的某个节点之类的操作上,比较麻烦。

所以,AQS在设计的时候通过双向链表和一定次数自旋的方式来解决上述的两个问题,具体请看系列的第五篇博客。

从上述所言,我们可以看出synchronized关键字在使用的时候其实是十分蛮横的,而且在很多时候的性能其实一言难尽。所以,Jvm本身对其有着很多的优化,具体是分为四种不同的状态,重量级锁,轻量级锁,偏向锁以及不用syn关键字的无锁状态。

重量级锁就是我们之前所言的,只能我拿,我放之前,你们吃土(阻塞)。

轻量级锁就是我拿了锁,有可能马上就释放了,所以你们可以先不用吃土了,你们自旋尝试拿锁一段时间(自旋次数不一定,但是总时间往往小于上下文切换时间,不然也不划算),如果拿到了,那就不用切换上下文浪费时间了。

而偏向锁呢?就是很多时候,一个线程占有绝对的主导权,绝大多数拿到锁的都是他,这些信息存储在对象头里,对象头里还会存储类似GC存活次数,对象的hashcode等信息。那么JVM完全可以通过对象头里的这些信息,在大多数时间里直接让这个线程用无锁方式执行任务,毕竟锁其实也是一种耗费资源的负担,能不用就不用吧。
Java多线程闲聊(六):synchronized关键字

当然,这几个状态是根据具体情况动态转换的,如果遇到恶劣的情况,偏向锁也会逐渐劣化变成轻量级锁,甚至重量级锁;遇到理想情况,则不断优化,反向而行之。
Java多线程闲聊(六):synchronized关键字


参考