synchronized锁原理详解
synchronized锁用法
// synchronized锁底层原理
public class SynchronizedTest03 {
//修饰静态方法(同步方法)
//代码块0(锁定的是当前类)
public synchronized static void access0(){
try {
TimeUnit.MINUTES.sleep(1);
System.out.println(Thread.currentThread().getName()+" is running");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//修饰非静态方法(同步方法)
//代码块1(锁定的是当前对象的调用者)
public synchronized void access1(){
try {
TimeUnit.MINUTES.sleep(1);
System.out.println(Thread.currentThread().getName()+" is running");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//修饰非静态方法(同步代码块)
//代码块2(锁定的是对象)this指的是当前对象
public void access2(){
synchronized(this){
try {
synchronized (this){
TimeUnit.MINUTES.sleep(1);
}
System.out.println(Thread.currentThread().getName()+" is running");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
//代码块3(锁定的是CLASS类)(同步代码块)
public void access3(){
synchronized(SynchronizedTest03.class){//ClassLoader class --> “堆”区生成一个Class对象:所有的对象
//有Class对象的所有的对象都共同使用这一个锁
try {
TimeUnit.MINUTES.sleep(1);
System.out.println(Thread.currentThread().getName()+" is running");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
SynchronizedTest03 demo = new SynchronizedTest03();
for (int i = 0; i < 5; i++) {
new Thread(demo::access2).start();
}
}
}
锁的分类:
1.对象锁
synchronized(this|object) \{\} 修饰非静态方法
在 Java 中,**每个对象都会有一个** **monitor** **对象**,这个对象其实就**是 Java 对象的锁**,通常会被**称为“内置锁”或“对象锁”**。类的对象可以有多个,所以每个对象有其独立的对象锁,互不干扰。
**monitor对象(监视器)**:
A、某一线程占有这个对象的时候,先看monitor 的计数器是不是0,如果是0还没有线程占有,这个时候线程占有这个对象,并且对这个对象的monitor+1;如果不为0,表示这个线程已经被其他线程占有,这个线程等待。当线程释放占有权的时候,monitor-1;
B、同一线程可以对同一对象进行多次加锁,加一次锁就monitor+1,可重入性。
2.类锁
synchronized(类.class) \{\} 修饰静态方法
在 Java 中,针对每个类也有一个锁,可以称为“**类锁**”,类锁实际上是通过对象锁实现的,即**类的 Class 对象锁**。每个类只有一个 Class 对象,所以每个类只有一个类锁。
synchronized原理分析
1、线程堆栈分析(Jconsole、Jstack pid)
上面的程序我们启动5个线程,每个线程睡眠1分钟,我们使用Jconsole软件进行查看线程。
我们可以看到有5个线程,一个线程正在等待,其他4个线程都是阻塞状态。
使用Jstack pid查看线程:
2、JVM指令分析
对当前类进行反编译:javap -v SynchronizedTest03
找到我们写的access0、access1、access2、access3方法:其中access0、access1原理相同(锁的方法),access2、access3原理相同(锁的代码块)
A、锁的代码块(用monitor进行加锁解锁)
【问题】为什么会有两个monitorexit?
答:第一个是正常出口,第二个是异常出口。
B、锁的方法(用标记进行加锁、ACC\_SYNCHRONIZED)
3、使用synchronized注意的问题
(1)与moniter关联的对象不能为空
(2)synchronized作用域太大
(3)不同的monitor企图锁相同的方法
(4)多个锁的交叉导致死锁
Java虚拟机对synchronized的优化
对锁优化加入:偏向锁、轻量级锁、重量级锁(等待时间长)
每一个monitor存放在一个实例对象里面,每一个对象都和monitor进行关联,主要与对象头进行关联,关联主要根据锁的状态进行。
无锁状态:没有加锁
偏向锁:在对象第一次被某一线程占有的时候,是否偏向锁置1,锁表01,写入线程号,当其他的线 程访问的时候,会去竞争,竞争失败(升级到轻量级锁)、很多次被第一次占有它的线程获取次数多—竞争成功
轻量级锁:线程有交替适用,互斥性不是很强,CAS失败,00
重量级锁:强互斥,10,等待时间长
自旋锁:竞争失败的时候,不是马上转化级别,而是执行几次空循环
锁消除:JIT在编译的时候吧不必要的锁去掉
还没有评论,来说两句吧...