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