-
1. 对象锁 类锁 非static方法加synchronized锁,static方法加synchronized锁,不加synchronized锁 三种调用顺序比较
public class TheadStaticLock { public synchronized void m1(){ System.out .println(Thread.currentThread().getName()+" m1 start"); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" m1 end"); } public synchronized static void m2(){ System.out.println(Thread.currentThread().getName()+" m2 start"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" m2 end"); } public void m3(){ System.out.println(Thread.currentThread().getName()+" m3 start"); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+" m3 end"); } public static void main(String[] args) { TheadStaticLock tl =new TheadStaticLock(); new Thread(()->tl.m1()).start(); new Thread(()->m2()).start(); new Thread(()->tl.m3()).start(); } }
看执行结果:
Thread-0 m1 start
Thread-1 m2 start
Thread-2 m3 start
Thread-1 m2 end
Thread-2 m3 end
Thread-0 m1 end
结论:说明m3 方法的执行并没有因为m1 m3方法的synchronzied对象锁而等待,而且m2方法没有因为m1方法的加锁而等待
m1方法synchronzied锁的是this对象,synchronzied他是一把对象锁,m2方法锁的是TheadStaticLock.class,我们知道static方法是没有this对象的,此时的synchronzied他是一把类锁,而m3方法压根就没有上任何锁。
2 可重入锁
同类型的锁类型在同类下是可重入的,他其实是一把锁,如果不是一把锁的话,互相等待那样就会发生死锁,Java肯定不能容忍这样的事情发生,因此这把锁是可重入锁定,锁上再加锁,上一把锁释放然后再给另一个同样持有这把锁的方法执行,看代码
public class ThreadReinLock {
private int count=10;
public synchronized void m1() throws InterruptedException {
System.out.println("m1实验可重入锁...start");
Thread.sleep(6000);
System.out.println("m1实验可重入锁...end");
m2();
}
public synchronized void m2() throws InterruptedException {
System.out.println("m2对象锁与m1是一把锁...start");
Thread.sleep(4000);
System.out.println("m2对象锁与m1是一把锁...end");
}
public synchronized static void m3() throws InterruptedException {
System.out.println("m3类锁与m1不是一把锁...,可以同时调用start");
Thread.sleep(1000);
System.out.println("m3类锁与m1不是一把锁...,可以同时调用end");
}
public String m4() throws InterruptedException {
System.out.println("m4类锁与m1不是一把锁...,可以同时调用start");
Thread.sleep(1000);
System.out.println("m4类锁与m1不是一把锁...,可以同时调用end");
return "";
}
public static void main(String[] args) throws InterruptedException {
ThreadReinLock trl=new ThreadReinLock();
new Thread(()-> {
try {
trl.m1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
new Thread(()-> {
try {
trl.m2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
m3();//静态方法是没有this对象的
new Thread(()-> {
try {
trl.m4();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
看执行结果:
m1实验可重入锁...start
m3类锁与m1不是一把锁...,可以同时调用start
m3类锁与m1不是一把锁...,可以同时调用end
m4类锁与m1不是一把锁...,可以同时调用start
m4类锁与m1不是一把锁...,可以同时调用end
m1实验可重入锁...end
m2对象锁与m1是一把锁...start
m2对象锁与m1是一把锁...end
m2对象锁与m1是一把锁...start
m2对象锁与m1是一把锁...end
结论:m1 和m2 是一把锁,且是重入锁,m1执行完才可以执行m2,static 不会影响m3执行不是一把锁 非static 非synchronized方法也没有上锁也可以同时执行
- 3 异常锁
异常锁,当锁方法在执行过程中发生异常,会释放掉锁 * thread0 在发生异常立刻释放对象锁 m2立刻拿到对象锁,立即执行
public class ThreadExceptionLock {
private int count=100;
synchronized void m1(){
System.out.println(Thread.currentThread().getName()+" m1 start");
for(int i=0;i<100;i++){
count--;
System.out.println(Thread.currentThread().getName()+" m1 count="+count);
if(count == 5){
System.out.println(1/0);
}
}
System.out.println(Thread.currentThread().getName()+" m1 end");
}
synchronized void m2() throws InterruptedException {
System.out.println(Thread.currentThread().getName()+" m2 start");
System.out.println("m2 拿到对象锁");
Thread.sleep(2000);
System.out.println(Thread.currentThread().getName()+" m2 end");
}
public static void main(String[] args) {
ThreadExceptionLock tel=new ThreadExceptionLock();
new Thread(()->tel.m1()).start();
new Thread(()-> {
try {
tel.m2();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
看执行结果:
Thread-0 m1 count=8
- Thread-0 m1 count=7
Thread-0 m1 count=6
Thread-0 m1 count=5
Thread-1 m2 start
m2 拿到对象锁Exception in thread "Thread-0"
java.lang.ArithmeticException: / by zero
at com.panmeng.learn.thread.ThreadExceptionLock.m1(ThreadExceptionLock.java:25)
at com.panmeng.learn.thread.ThreadExceptionLock.lambda$main$0(ThreadExceptionLock.java:39)
at java.lang.Thread.run(Thread.java:748)
Thread-1 m2 end
结论:当拿到synchronized的m1方法在执行过程中由于各种原因发生了异常,是立刻马上释放synchronized锁,从而m2方法立刻获取了synchronized锁,如果不想释放锁,可以在代码外围加上try catch代码块捕获异常,这样就不会释放锁
- 偏向锁 自旋锁 重量锁
synchronized 方法在刚开始执行的时候,jvm会分配一把偏向锁(markword标记一下线程ID),偏向这个方法,会哭的娃娃有奶吃,cpu重点照顾下这个方法,如果在4秒内出现了其他同类型的synchronized方法,线程来争用当前持有锁对象的锁,好的,进行锁升级,升级为自旋锁,自旋锁也就是开启一个轮询盯着当前持有锁对象是否已执行完,当轮询10次以后,这个方法还是没有释放锁对象,好吧,再次进行锁升级,升级为重量级锁,交给操作系统OS进行锁维护,其实在jdk1.5版本之前synchronized锁粒度较大,上来就重量级锁,性能极差。后来进行了优化后,其实性能和atomic已经相当,甚至要优于他们,因为atomic技术使用的是自旋锁,而synchronized默认是偏向锁,更加轻量级,以上是对synchronized的底层原理设计的一些看法。
锁应用场景:
自旋锁:加锁代码执行时间短,线程数比较少(1-2),因为系统不允许大量自旋锁一直消耗cpu资源进行自旋
系统锁(重量级锁): 加锁代码执行时间较长,线程数比较多(>=3),