Synchronized原理

                                                                        Synchronized的底层理解

1,monitor是一个计数器

2,为什么会有Synchronized关键字:

     当程序出现并发的时候,访问修改一个共享变量的时候就会出现线程安全问题,Synchronized的出现就是为了解决线程安全问题

3,锁机制的两种特性:

      互斥性:同一个时间点只允许某一个线程持有某个对象锁,如果其他线程需要获取到同样的对象锁就必须等到前一个线程释放。

      可见性:在锁被释放之前,对共享数据做出的改变必须让其他线程感知到,及时获取最新数据,防止脏数据。

 

Synchronized(this||object对象)

 在Java中每一个对象都有一个moniter对象,这个对象其实就是Java对象的锁,通常我们称为内置锁或对象锁,一个类有多个对象,所以每个对象都有独立的锁互不干扰。

Synchronized(object.class)

在Java中,针对每一个类都有一个锁,我们称为类锁,实际上类锁也是通过对象锁实现的,即类的class对象,每一个类只有一个class对象,所以只有一个类锁。

Java里面每一个对象都有已个monitor对象,monitor我们也可以理解为监视器

某一个线程占有这个对象的时候monitor就会进行+1,其他线程发现monitor不为0的时候就会进行等待,直到上一个线程释放这个对象monitor进行-1为0的时候,才进行执行

同一个线程可以对同一个对象进行多次加锁,重入锁,当这个线程第二次对同一个对象加锁的时候monitor还是会+1

Synchronized的原理分析

      线程堆栈的分析互斥性:

当thread-0线程先获取到同一个锁的时候,他的状态是TIME_WATTING,其他线程则BLOCKD,阻止其他线程进行访问

thread-1没有获取到锁那么他的状态为BLOCKD,等待对象释放

所以他就有了互斥性,当thread-1去获取锁的时候,发现有线程已经获取了,他的总阻止数为1.

 

    JVM指令分析:

             Sycchronized同步代码块

当Synchronized同步代码块的时候我们发现,他是使用moniter计数器来进行标记

monitorenter代表互斥的开始 moniter进行+1操作

monitorexit代表互斥的结束moniter进行-1操作(正常退出)

这里有两个monitorexit 前者为正常出口,后者为异常出口

 

Sycchronized同步方法

当Synchronized同步方法的时候,我们发现他并没有monitorenter与monitorexit两个关键字,那他是怎么进行标识的呢?

Synchronized同步方法的时候,他又一个特殊的ACC_SYNCHRONIZED标记,来标识这个互斥的开始。LineNumberTable代表互斥的结束。

 

以上就是Synchronized在使用的时候,他的底层又一些什么动作。

 

使用Synchronized注意的问题:

       1 与moniter关联的对象不能为空

        2 Synchronized的作用域比较大要谨慎使用,一个类集成了thread,重写了run方法那么再给run方法进行加锁就会导致作用域过大

       3  不同的moniter企图锁不同的对象

       4 多个锁交叉出现死锁

 

实列对象与monitor的关系

对象头中主要存放数据为:加锁的数据,他又分为下列三个区域

当一个Java对象被某一个线程占用的时候,则该java对象的Mark Word字段中LockWord指向monitor的起始地址

实列变量主要存放的是一些数据,比如int =1  int =2

填充数据暂时不管

   

 

Java虚拟机对Synchronized优化

Mark Word里面存放的锁状态

       在JDK1.6之前就只有重量锁

       在JDK1.6之后就新加入了,偏向锁,轻量级锁,自旋锁

      偏向锁:

           某个对象第一次被某个线程占有的时候,他会把是否为偏向锁置为1,记录线程id,当下一次这个线程再来的时候他就不需要经过CAS算法了,这样竞争到锁的几率会大一点。

           如果线程竞争失败了,被其他线程占有的时候,就会把锁的状态提升为轻量级锁,Mark Word里面的锁标记改为00。当然从偏向锁提升到轻量级锁的时候,也会由用户线程转到内核线程是需要耗费很长时间的。所以就加入了自旋锁,当A线程为第一次占有对象的时候,第二次竞争线程失败,不会由偏向锁直接转为轻量级锁,他会先使用自旋锁执行几次空循环(默认为10次),如果他发现执行完自旋锁之后对象还是被占有的时候,就会升级为轻量级锁。

           偏向锁,适合在竞争小的情况下使用,不需要每次都经过CAS算法,直接去获取到monitor对象,这样等待的时间很短,接近于无锁时间。

     轻量级锁:

            从偏向锁升级为轻量级锁的时候,JVM会向栈帧中开辟一块空间(Lock Record),然后拷贝Mark Word的信息到Lock Record中,如果只有一个线程进行等待就会进行自选,可能稍微等待一下,持有资源的线程就会释放,但是超过                                                                  Synchronized的底层理解

 

1,monitor是一个计数器

 

2,为什么会有Synchronized关键字:

 

     当程序出现并发的时候,访问修改一个共享变量的时候就会出现线程安全问题,Synchronized的出现就是为了解决线程安全问题

 

3,锁机制的两种特性:

 

      互斥性:同一个时间点只允许某一个线程持有某个对象锁,如果其他线程需要获取到同样的对象锁就必须等到前一个线程释放。

 

      可见性:在锁被释放之前,对共享数据做出的改变必须让其他线程感知到,及时获取最新数据,防止脏数据。

 

 

 

Synchronized(this||object对象)

 

 在Java中每一个对象都有一个moniter对象,这个对象其实就是Java对象的锁,通常我们称为内置锁或对象锁,一个类有多个对象,所以每个对象都有独立的锁互不干扰。

 

Synchronized(object.class)

 

在Java中,针对每一个类都有一个锁,我们称为类锁,实际上类锁也是通过对象锁实现的,即类的class对象,每一个类只有一个class对象,所以只有一个类锁。

 

Java里面每一个对象都有已个monitor对象,monitor我们也可以理解为监视器

 

某一个线程占有这个对象的时候monitor就会进行+1,其他线程发现monitor不为0的时候就会进行等待,直到上一个线程释放这个对象monitor进行-1为0的时候,才进行执行

 

同一个线程可以对同一个对象进行多次加锁,重入锁,当这个线程第二次对同一个对象加锁的时候monitor还是会+1

 

Synchronized的原理分析

 

      线程堆栈的分析互斥性:

 

 

 

当thread-0线程先获取到同一个锁的时候,他的状态是TIME_WATTING,其他线程则BLOCKD,阻止其他线程进行访问

 

thread-1没有获取到锁那么他的状态为BLOCKD,等待对象释放

 

所以他就有了互斥性,当thread-1去获取锁的时候,发现有线程已经获取了,他的总阻止数为1.

 

 

 

    JVM指令分析:

 

             Sycchronized同步代码块

 

 

当Synchronized同步代码块的时候我们发现,他是使用moniter计数器来进行标记

 

monitorenter代表互斥的开始 moniter进行+1操作

 

monitorexit代表互斥的结束moniter进行-1操作(正常退出)

 

这里有两个monitorexit 前者为正常出口,后者为异常出口

 

 

 

Sycchronized同步方法

 

 

当Synchronized同步方法的时候,我们发现他并没有monitorenter与monitorexit两个关键字,那他是怎么进行标识的呢?

 

Synchronized同步方法的时候,他又一个特殊的ACC_SYNCHRONIZED标记,来标识这个互斥的开始。LineNumberTable代表互斥的结束。

 

 

 

以上就是Synchronized在使用的时候,他的底层又一些什么动作。

 

 

 

使用Synchronized注意的问题:

 

       1 与moniter关联的对象不能为空

 

        2 Synchronized的作用域比较大要谨慎使用,一个类集成了thread,重写了run方法那么再给run方法进行加锁就会导致作用域过大

 

       3 不同的moniter企图锁不同的对象

 

       4 多个锁交叉出现死锁

 

 

 

实列对象与monitor的关系

 

 

对象头中主要存放数据为:加锁的数据,他又分为下列三个区域

 

当一个Java对象被某一个线程占用的时候,则该java对象的Mark Word字段中LockWord指向monitor的起始地址

 



 

实列变量主要存放的是一些数据,比如int =1 int =2

 

填充数据暂时不管

 

   

 

 

 

Java虚拟机对Synchronized优化

 

Mark Word里面存放的锁状态

 



 

       在JDK1.6之前就只有重量锁

 

       在JDK1.6之后就新加入了,偏向锁,轻量级锁,自旋锁

 

      偏向锁:

 

           某个对象第一次被某个线程占有的时候,他会把是否为偏向锁置为1,记录线程id,当下一次这个线程再来的时候他就不需要经过CAS算法了,这样竞争到锁的几率会大一点。

 

           如果线程竞争失败了,被其他线程占有的时候,就会把锁的状态提升为轻量级锁,Mark Word里面的锁标记改为00。当然从偏向锁提升到轻量级锁的时候,也会由用户线程转到内核线程是需要耗费很长时间的。所以就加入了自旋锁,当A线程为第一次占有对象的时候,第二次竞争线程失败,不会由偏向锁直接转为轻量级锁,他会先使用自旋锁执行几次空循环(默认为10次),如果他发现执行完自旋锁之后对象还是被占有的时候,就会升级为轻量级锁。

 

           偏向锁,适合在竞争小的情况下使用,不需要每次都经过CAS算法,直接去获取到monitor对象,这样等待的时间很短,接近于无锁时间,但是在锁升级的情况下会耗费CPU资源

 

     轻量级锁:

 

            从偏向锁升级为轻量级锁的时候,JVM会向栈帧中开辟一块空间(Lock Record),然后拷贝Mark Word的信息到Lock Record中,如果只有一个线程进行等待就会进行自选,可能稍微等待一下,持有资源的线程就会释放,但是超过一定的次数之后,或者一个线程在持有资源,一个线程在等待,第三个线程来访问的时候就向重量锁转变

           轻量级锁,适合在线程交替的情况下使用,同步执行的时候很快,但是如果始终得不到锁资源,那么自旋的线程就会耗费资源

 

 

      锁粗化:

           StringBuffer sb = new StringBuffer();

           sb.append("1");

           sb.append("2");

           StringBuffer是线程安全的,他的append方法是加锁的,调用两个append方法就会出现锁粗化机制,扩大锁的范围使用一个Synchronized包裹两个append方法,这样不会加锁两次。

 

      锁消除:

            

            JVM在编译的时候会去除一些没有必要锁,int=1本来就具有原子性的时候,加锁是没有意义的,JVM在编译的时候就会帮我们消除这样的锁。

 

      

 

 

 

https://www.cnblogs.com/bjlhx/p/10555194.html

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值