java并发与多线程(三):什么是锁

计算机的锁分类有很多种,本书并不打算详细介绍每种锁,而是通过对java.util.concurrent(JUC)包中的基础类的解析来说明锁的本质和特性。Java中常用锁实现的方式有两种。

1、用并发包中的锁类

并发包的类族中,Lock是JUC包的顶层接口,它的实现逻辑并未用到synchronized,而是利用volatile的可见性。先通过Lock来了解JUC包的一些基础类,如图所示:


image.png

图为Lock的继承类图,ReentrantLock对于Lock接口的实现主要依赖了Sync,而Sync继承了AbstractQueuedSynchronizer(AQS),它是JUC包实现同步的基础工具。在AQS中,定义了一个volatile int state变量作为共享资源,如果线程获取资源失败,则进入同步FIFO队列中等待;如果成功获取资源就执行临界区代码。执行完释放资源时,会通知同步队列中的等待线程来获取资源后出队并执行。

AQS是抽象类,内置自旋锁实现的同步队列,封装入队和出队的操作,提供独占共享、中断等特性的方法。AQS的子类可以定义不同的资源实现不同性质的方法。比如可重入锁ReentrantLock,定义state为0时可以获取资源并置为1.若以获得资源,state不断加1,在释放资源时state减1,直至为0;CountDownLatch初始时定义了资源总量state=count,countDown()不断将state减1,当state=0时才获得锁,释放后state就一直为0。所有线程调用await()都不会等待,所以CountDownLatch是一次性的,用完后如果再想用就只能重新创建一个;如果希望循环使用,推荐使用基于ReentrantLock实现的CyclicBarrier。Semaphore与CountDownLatch略有不同,同样也定义了资源总量state=permits,当state>1时就能获得锁,并将state减1,当state=0时只能等待其他线程释放锁,当释放锁时state加1,其他线程又能获得这个锁。当Semphore的permits定义为1时,就是互斥锁,当permits>1时,就是共享锁。

JDK提出了一个新的锁:StampedLock,改进了读写锁ReentrantReadWriterLock。这些新增的锁相关类不断丰富了JUC包的内容,降低了并发编程的难度,提高了锁的性能和安全性。

2、利用同步代码块

同步代码块一般使用Java的synchronized关键字来实现,有两种方式对方法进行加锁操作:第一,在方法签名处加synchronized关键字;第二,使用synchronized(对象或类)进行同步。这里的原则是锁的范围尽可能小,锁的时间尽可能短,即能锁对象,就不要锁类;能锁住代码块,就不要锁方法。

Synchronized锁特性由JVM负责实现。在JDK的不断优化迭代中,synchronized锁的性能得到极大提升,特别偏向锁的实现,使得synchronized已经不是昔日那个低性能且笨重的锁了。JVM底层是通过监视锁来实现synchronized同步的。监视所即monitor,是每个对象与生俱来的一个隐藏字段。使用synchronized时,JVM会根据synchronized的当前使用环境,找到对应对象的monitor,再根据monitor的状态进行加、解锁的判断。例如,线程在进入同步方法或代码块时,会获取该方法或代码块所属对象的monitor,进行加锁判断。如果成功加锁就成为该monitor的唯一持有者。Monitor在被释放前,不能再被其他线程获取。下面通过字节码学习synchronized锁时如何实现的:


image.png
image.png

方法元信息中会使用ACC_SYNCHRONIZED标识该方法是一个同步方法。同步代码块中会使用monitorenter及monitorexit两个字节码指令获取和释放monitor。如果使用monitorenter进入时monitor为0,表示该线程可以持有monitor后续代码。并将monitor加1;如果当前线程已经持有了monitor,那么monitor继续加1;如果monitor非0,其他线程就会进入阻塞转态。JVM对synchronized的优化主要在于对monitor的加锁、解锁上。JDK6后不断优化使得synchronized提供三种锁的实现,包括偏向锁、轻量级锁、重量级锁,还提供自动的升级和降级机制。JVM就是利用CAS在对象头上设置线程ID,表示这个对象偏向于当前线程,这就是偏向锁。

偏向锁是为了在资源没有被多线程竞争的情况下尽量减少锁带来的性能开销。在锁对象的对象头中有一个ThreadId字段,当第一个线程访问锁时,如果该锁没有被其他线程访问过,即ThreadId字段为空,那么JVM让其持有偏向锁,并将ThreadId字段的值设置为线程的ID。当下一次获取锁时,会判断当前线程的ID是否与锁对象的ThreadId一致。如果一致,那么该线程不会再重复获取锁,从而提高了程序的运行效率。如果出现锁的竞争情况,那么偏向锁会被撤销并升级为轻量级锁。如果资源的竞争非常激烈,会升级为重量级锁。偏向锁可以降低无竞争开销,它不是互斥锁,不存在线程竞争情况,省去再次同步判断的步骤,提升了性能。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,590评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,808评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,151评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,779评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,773评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,656评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,022评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,678评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,038评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,756评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,411评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,005评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,973评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,053评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,495评论 2 343

推荐阅读更多精彩内容