java多线程(四) java中的锁-synchronized关键字

一、写作议题:

  • 一个问题:为什么要使用锁机制,锁机制是解决了一个什么问题,这里我在维基百科上拷贝了一下线程安全的一个基本概论;
Thread safety is a computer programming concept applicable to multi-threaded code. 
Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction. There are various strategies for making thread-safe data structures.

A program may execute code in several threads simultaneously in a shared address space where each of those threads has access to virtually all of the memory of every other thread. Thread safety is a property that allows code to run in multithreaded environments by re-establishing some of the correspondences between the actual flow of control and the text of the program, by means of synchronization
  • 由于cpu的串行特点,在访问共享数据和共享内存的时候,势必会造成一些数据竞争,导致程序不能按照我们预想的顺序进行,所以java利用锁机制来实现多线程情况下的同步。

二、java中的锁机制

  • java内置的锁,同步原语synchronized的关键字
  • 在jDK1.5以后引入的lock接口。
    本篇主要介绍一下我对synchronized关键字的一些理解,查阅了一些资料,后续我也会分享出来。

三、深入理解synchronized关键字。

  • synchronized关键字是java并发同步开发的基本技术,是java语言层面上提供的同步技术;
  • 下面一段代码来了解一下,java对于synchronized关键字,在编译以后会发生什么操作。
public class Syn {
    private static final Object lock=new Object();
    private static int a=10;
   public static void main(String[] args) {
        int a=0;
        synchronized (lock){
            a++;
        }
        System.out.println("Result"+a);
    }
    public static synchronized  int getNum(int increse){
        a=increse+a;
        return a;
    }

现在使用javap命令了解一下,针对同步的部分生成的字节码;

      7: **monitorenter**
       8: iinc          1, 1
      11: aload_2
      12: **monitorexit**
      13: goto          21
      16: astore_3
      17: aload_2
      18: **monitorexit**

具体monitorenter和monitorexit到底干了什么事?jvm需要保证monitorenter和monitorexit一一配对,任何一个对象都有一个monitor与之关联,当且一个monitor被持有以后,它将处于锁定的状态,线程执行到monitorenter指令以后,将会尝试获取对象所对应的monitor的所有权,即尝试获取对象的锁,有兴趣的同学可以了解一下jdk的源码。找到这几个函数主要干了什么工作。

  • 这里直接给出答案,java在jdk1.6引入了偏向锁,大大的提升了synchronized关键字的性能,这里synchronized不在是我们所说的重锁,性能底下,说它鸟枪换炮也不为过。
    这里贴出java官方文档openJdkwiki
    锁优化

    第一次看这个图的时候,也是懵逼状态,查阅了一些资料和一些讲解后有了一点自己的理解。
  • 知识补充
    想要了解这张图可能需要了解一些其他的java知识;
    1、java对象的内存布局:java对象在内存中分为对象头和对象体,对象头中存储对象的特征数据,对象体中存储对象的数据部分,那我们如何查看对象的内存布局了,其实java提供了一个工具包交叫jol,可以帮组我们了解java对象的内存布局。
step1:
//首先引入jol的maven依赖
  <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.9</version>
        </dependency>
step2:
//编写一个简单的java类,调用jol的方法,打印对象的内存分布
public class JavaObject {
    public static void main(String[] args) {
        System.out.println(VM.current().details());
        System.out.println(ClassLayout.parseClass(A.class).toPrintable());
    }
    public static class A{
        public boolean f;
        public Double d;
    }
}
step3:
分析运行结果:

运行结果截图:


控制台输出

4种锁,对象内存的分布情况。


在这里插入图片描述

通过截图可以看出,JavaObejct对象的对象头占12个字节,f字段为boolen类型字段,占一个字节,因为java是8字节对齐(Objects are 8 bytes aligned),所以需要3字节的填充字段,Double类型占占4个字节,加上填充字段4个字节,所以这个对象的整体长度为24字节。
  • 现在就来分析一下openJdk的那一幅图,首先需要一个名次解释,jdk1.6引入的偏向锁,也可以叫做偏置锁是什么意思?所谓偏置就是就是偏向某一个线程的意思。
  • 偏向锁的由来:java的编写者发现大多数情况下锁不仅不存在多线程竞争,而且总是由同一线程多次获取,偏向锁的目的是在某个线程获取锁之后,消除这个线程重入的开销,看起来是对这个线程又一个偏心的操作。
    传统的锁过程是一个重量级锁,所谓重量级锁,其实是一个悲观锁的表现,会导致线程状态的改变,而线程状态的改变其实是很重量级的,下来我们就可以分析一个jdk1.6以后引入的锁优化技术,默认的使用了偏向锁。
  • 上图分析,从左边开始,java1.6+就按照以上的逻辑进行锁优化,首先
    java在分配对象的时候,会有下面两种情况,但是默认jdk1.6以后开启了优化技术,所以我们就从左边开始讲起。
    1、if biased locking is enable for the class;(如果偏向锁可用)
    无锁状态:如果一个对象处于未锁定,未偏置,但是可以偏置的状态,对象头中会存储
    1 |01 1表示可以偏置(默认开启)。01表示未锁定;

2、unlocked and unbiased but biasable object
初始化锁定:(偏向锁的一个获取)
这个时候如果有一个线程来获取锁此时这个对象就马上变成了偏置状态,其实对比上一个状态,改变的就是在对象头的栈帧中多了线程的线程ID,其实也就是官宣了,说明我这个对象目前已经是名花有主的人了。当相同的线程再次来获取这个锁的时候,其实这里就不需要在执行CAS操作,只需要一次指针的对比,而指针的对比是很轻量级的,如果对比成功,就继续往下执行,并该线程持有这个锁,如果这个时候有另外一根线程来竞争这个锁,指针的对比就失败那么就发生以下两种情况。结合上面的图我们可以分析;

一、假如持有锁的为线程1,线程2此时来竞争锁,当线程2到达的时候,要么线程1已经释放了锁,处于一个解锁的状态,此时对象的状态就是偏置1,未锁定。但是偏向锁还是存在的,并且线程的id可能还是线程1。

二、如果此时线程1正好持有这个锁,那么此时对象的状态为偏置1,已锁定。

我们结合上图分析一个这两种情况,情况一,如果当前锁对象的状态为不可偏置,未锁定,虚拟机将会执行CAS操作,尝试更新指向线程2,也就是撤销偏向的操作。如果成功的话,那么线程2就持有这个锁对象。
并且后面的标志位变为00,表示轻量级锁定。

3、情况二、说明目前的锁是处于锁定的状态,这时候仍然会升级为轻量级锁,但是此时线程2会获取锁失败,因此这个锁会进行膨胀操作,变为一个重量级锁。
总结:在一个线程轻量级锁定某个对象的时候,如果出现竞争也会导致锁定膨胀,变成重量级锁,也就是没有竞争的偏向锁,没有竞争或者少量竞争就是轻量级锁,大量竞争就会变成重量级锁。

轻量级锁升级为重量级锁的流程图:


流程图

三、锁的分类

1、概念上的锁分类:乐观锁和悲观锁我常常听见,现在来个名次解释说明一下,也是记录一下自己对此的理解:

  • 悲观锁:就是持有一种悲观的心态,就是认为如果我们不考虑线程安全,不做充分的同步工作就肯定会出问题,举个例子,就是我们不管等会会不会下雨,我们都穿上雨衣,雨鞋,雨伞。java传统的同步手段,包括synchronized关键字,可重入锁。
  • 乐观锁:会乐观的估计系统当前的状态,认为状态是符合预期的,如果发生竞争,就退避,然后进行一定的策略,比如我们先看天气预报,万一天气预报不准,我们可以选择planB。
    java的CAS操作,其实就是一种乐观的表现。

四、总结:

上述介绍的锁,不是说越轻量级就性能越好,对于不同的锁场景,选择其有点的锁,会大大增加效率。
1、对于偏向锁,加速和解锁不需要额外的消耗,但是如果线程间存在锁的竞争,会带来额外的锁撤销消耗:
2、对于轻量级锁,竞争的线程不会阻塞,但是一个线程始终得不到锁,会一直自旋消耗cpu;
3、重量级锁:线程竞争不会使用自旋,但是线程阻塞,相应时间很慢。

五、参考文章:

1、锁优化技术
2、openJDK
3、infoQ

感谢各位大佬的无私分享

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

推荐阅读更多精彩内容