JVM(5)-线程安全与锁优化

2018-05-17

1.线程安全

如果一个对象能安全地被多个线程同时使用,那么它就是线程安全的。

当多个线程访问同一个对象时,如果不需要考虑这些线程在运行时环境下的调度和交替执行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都能得到正确的结果,那这个对象就是是线程安全的。

2.Java语言中的线程安全

2.1不可变

JDK1.5后,不可变(Immutable)对象一定是线程安全的,注意final修饰的基本数据类型是不可变的,但是引用类型只能保证一级不可变,即当前引用不可再赋值,但引用的对象的非final属性是可以修改的。

2.2绝对线程安全

无论怎么使用,都是线程安全的,满足上面第二个定义。

2.3相对线程安全

比如Vector,虽然它的方法加了synchronized关键字,但并不能说它就是绝对安全的。

2.4线程兼容

对象本身不是线程安全的,但是可以通过一定的同步来实现线程安全,例如synchronized。

2.5线程对立

如 Thread.suspend()和Thread.resume()

3.线程安全的实现方法

同步是指在多线程并发访问共享数据时,保证同一时刻只被一个(或者是一些,使用信号量时)线程使用。

3.1互斥同步

互斥是实现同步的手段,临界区、互斥量、信号量都是主要的互斥实现方式。Java中最基本的互斥手段就是synchronized关键字,synchronized关键字在编译后,会在同步块前后分别形成monitorenter和monitorexit指令。这两个指令需要一个reference类型的参数来指明要锁定和解锁的对象。如果synchronized明确指定了对象参数,那就是这个对象的reference,如果没有指定,那就要区分是实例方法还是类方法,取当前对象或者这个类对应的Class对象。

3.2非阻塞同步

互斥同步的主要问题是进行线程的阻塞和唤醒所带来的性能问题,因为这种方式会阻塞其他线程,因此也可以称为阻塞同步。从处理方式上来看,属于悲观的并发策略,即不管有没有竞争,都进行同步。
随着硬件指令系统的发展(需要一些原子操作指令的支持,例如CAS,早期的计算机指令系统可能没有这样的指令),有了另一个选择,基于冲突检测的乐观并发策略,即先进形操作,如果没有其他线程争用共享数据,那就操作成功了,如果存在竞争,再采取必要的补救措施,比如不断地重试,直到成功为止。

4.锁优化

4.1自旋锁与自适应自旋

由于挂起线程和唤醒线程需要切换的内核状态进行,这是个不小的开销。虚拟机的开发团队研究发现许多应用,共享锁的锁定转态通常是持续很短的时间,所以可以让在等待获锁的线程不进入阻塞状态,而是继续执行自旋操作,稍等一下就能获得锁,这样做在一定程度上避免用户态与内核态的切换,但自旋的线程会继续占用CPU时间片。

自旋时间的选择也是很关键的,自旋多久后仍然没有获得锁就进入阻塞状态?所以在JDK1.6后,引入了自适应的自旋锁,由前一次在同一个锁上的自旋时间和锁的拥有线程的状态决定。

4.2锁消除

虚拟机即时编译器在运行时,对一些代码上要求同步,但被检测到不可能存在数据竞争的锁进行清除。这种通常不是开发人员自己写出来的,举一个例子:

    public String concatString(String s1, String s2, String s3) {
        return s1 + s2 + s3;
    }

由于String是一个不可变的类,在字符串连接时都是创建一个新的String对象来完成的,因此,javac编译器对String对象连接做了自动化。在JDK1.5以前,是通过StringBuffer来完成,而JDK1.5及之后,是通过StringBuilder来完成。那JDK1.5之前上面的代码就会变成:


    public String concatString(String s1, String s2, String s3) {
        StringBuffer sb = new StringBuffer();
        sb.append(s1);
        sb.append(s2);
        sb.append(s3);
        return sb.toString();
    }

而StringBuffer的append()方法是加了synchronized关键字的同步方法,但是很显然这种情况下的同步是完全没有必要的,所以虚拟机将这种锁清除掉以提高性能。

4.3锁粗化

同步有一个原则是,让同步块尽量小一下,一般情况下是正确的,但如果一系列的操作都对同一个对象进行加锁解锁,会带来不小的性能开销,这种情况还不如把同步范围扩大至一系列操作之前,这样只需要加/解锁一次就行了。

4.4轻量锁

前面《运行时数据区》一文中提到对象的内存布局包括3部分:

对象头(Header)
实例数据(Instance Data)
对齐填充(Padding)非必须

HotSpot虚拟机的对象头(Object Header)包含两部分的信息:

  • 第一部分用户存储对象自身的运行时数据,如 HashCode、GC分代年龄(Generational GC Age)等,这部分数据在32bit和64bit的虚拟机中分别为32bit和64bit,官方称它为“Mark Word”,它是轻量级锁和偏向锁的关键;
  • 另外一部分用于存储方法区对象类型的引用,如果是数组对象的话,还会有一个额外的部分用于存储数组的长度。

32bit HotSpot虚拟机下的对象状态,为锁定状态下Mark Word 32bit中,25bit是HashCode,4bit是对象分代年龄,2bit是标记(例如未锁定是01),1bit固定为0。

存储内容 标记位 状态
对象哈希码,分代年龄 01 未锁定
指向锁记录的指针 00 轻量级锁定
指向重量级锁的指针 10 膨胀(重量级锁定)
空,不需要记录信息 11 GC标记
偏向线程ID,偏向时间戳,对象分代年龄 01 可偏向
  1. 在代码进入同步代码块时,如果对象是未锁定状态,虚拟机会首先会在当前线程的栈帧中创建一个锁记录(Locked Record)空间,用于存储锁对象当前的Mark Word拷贝,叫Displaced Mark Word;
  2. 采用CAS操作将锁对象的Mark Word修改为指向锁记录的指针,如果更新成功,那线程就拥有了该锁对象,并且对象的Mark Word的标记位(最后2bit)修改为00。
Mark Word

轻量级锁提升性能的依据是:对于绝大部分的锁,同步过程是不存在竞争的。如果没有竞争,那轻量级的CAS操作避免了互斥量的开销,但如果存在竞争,那性能反而传统的重量级锁慢(CAS+互斥信号量)。

4.5偏量锁

如果说轻量级锁是在无竞争条件下,通过CAS操作去消除的同步使用互斥信号量,那偏向锁就是在无竞争条件下把整个同步都消除掉,连CAS也不用做。

“偏”指的是这个锁对象会偏向第一个获取它的线程,如果在接下来的的执行过程中,该锁没有被其他线程获取,则持有该偏向锁的线程将永远不再需要同步。

当锁对象第一次被获取时,标记为被设为“01”,即偏向模式,并且把获取到这个锁的线程ID记录在Mark Word中,后面持有偏向锁的线程在进入同步代码块,就不需要再同步了。

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

推荐阅读更多精彩内容