锁重入
synchronized拥有锁重入的机制,也就是在使用synchronized时候,当一个线程得到对象锁之后,再次请求此对象锁时是可以再次得到该对象的锁的。简单说就是synchronized修饰的方法或者代码块内部调用本类的其他synchronized方法或者代码块,是永远可以得到锁的。当一条线程获得了对象锁,此时该线程还没有释放该对象锁,当其再次获取这个对象锁的时候还是可以获取的,如果没有这样的设计,就会造成死锁。
以上解释摘抄于机械工业出版社《Java多线程编程核心技术》
示例很简单,如下:
package com.leolee.multithreadProgramming.test.sunLockIn1;
/**
* @ClassName Service
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Service {
public synchronized void service1() {
System.out.println("service1");
service2();
}
public synchronized void service2() {
System.out.println("Service2");
service3();
}
public synchronized void service3() {
System.out.println("service3");
}
}
package com.leolee.multithreadProgramming.test.sunLockIn1;
/**
* @ClassName MyThread
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class MyThread extends Thread {
@Override
public void run() {
Service service = new Service();
service.service1();
}
}
package com.leolee.multithreadProgramming.test.sunLockIn1;
/**
* @ClassName Run
* @Description: 锁重入
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
运行结果:
service1
Service2
service3
三个同步方法串联调用,可以正常打印就说明了锁重入的机制,第二第三个同步方法获取的对象锁和第一个同步方法获得的对象锁是同一个。
锁重入也支持在父子类继承的情况下:
package com.leolee.multithreadProgramming.test.synLockIn2;
/**
* @ClassName Main
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Main {
public int i = 10;
public synchronized void operateIMainMethod() {
try {
i--;
System.out.println("main print i=" + i);
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.leolee.multithreadProgramming.test.synLockIn2;
/**
* @ClassName Sub
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Sub extends Main {
public synchronized void operateISubMethod() {
try {
while (i > 0) {
i--;
System.out.println("sub print i=" + i);
Thread.sleep(100);
this.operateIMainMethod();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.leolee.multithreadProgramming.test.synLockIn2;
/**
* @ClassName MyThread
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class MyThread extends Thread {
@Override
public void run() {
Sub sub = new Sub();
sub.operateISubMethod();
}
}
package com.leolee.multithreadProgramming.test.synLockIn2;
/**
* @ClassName Run
* @Description: 重入锁支持在子父类继承的情况下
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Run {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
}
}
结果如下:
sub print i=9
main print i=8
sub print i=7
main print i=6
sub print i=5
main print i=4
sub print i=3
main print i=2
sub print i=1
main print i=0
子类中的同步方法可以调用父类中的同步方法证明了锁重入,结果中i的顺序递减证明了锁重入的线程安全性。
出现异常,锁自动释放
package com.leolee.multithreadProgramming.test.throwExceptionNoLock;
/**
* @ClassName Service
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Service {
public synchronized void testMethod() {
if (Thread.currentThread().getName().equals("a")) {
System.out.println("ThreadName=" + Thread.currentThread().getName() + " beginTime=" + System.currentTimeMillis());
while (true) {
if (String.valueOf(Math.random()).substring(0, 8).equals("0.123456")) {//模拟业务处理的时间不确定性
System.out.println("ThreadName=" + Thread.currentThread().getName() + " exceptionTime=" + System.currentTimeMillis());
Integer.valueOf("a");
}
}
} else {
System.out.println("Thread B run time=" + System.currentTimeMillis());
}
}
}
package com.leolee.multithreadProgramming.test.throwExceptionNoLock;
/**
* @ClassName ThreadA
* @Description: TODO
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class ThreadA extends Thread {
private Service serive;
public ThreadA(Service service) {
super();
this.serive = service;
}
@Override
public void run() {
serive.testMethod();
}
}
package com.leolee.multithreadProgramming.test.throwExceptionNoLock;
/**
* @ClassName ThreadB
* @Description: TODO
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class ThreadB extends Thread {
private Service serive;
public ThreadB(Service service) {
super();
this.serive = service;
}
@Override
public void run() {
serive.testMethod();
}
}
package com.leolee.multithreadProgramming.test.throwExceptionNoLock;
/**
* @ClassName Run
* @Description: 运行中出现异常,锁自动释放
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Run {
public static void main(String[] args) {
try {
Service service = new Service();
ThreadA threadA = new ThreadA(service);
threadA.setName("a");
threadA.start();
Thread.sleep(500);
ThreadB threadB = new ThreadB(service);
threadB.setName("b");
threadB.start();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
多次执行结果如下:
ThreadName=a beginTime=1598514047758
ThreadName=a exceptionTime=1598514048060
Exception in thread "a" java.lang.NumberFormatException: For input string: "a"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at com.leolee.multithreadProgramming.test.throwExceptionNoLock.Service.testMethod(Service.java:18)
at com.leolee.multithreadProgramming.test.throwExceptionNoLock.ThreadA.run(ThreadA.java:21)
Thread B run time=1598514048273
ThreadName=a beginTime=1598514201617
ThreadName=a exceptionTime=1598514202174
Thread B run time=1598514202175
Exception in thread "a" java.lang.NumberFormatException: For input string: "a"
at java.lang.NumberFormatException.forInputString(NumberFormatException.java:65)
at java.lang.Integer.parseInt(Integer.java:580)
at java.lang.Integer.valueOf(Integer.java:766)
at com.leolee.multithreadProgramming.test.throwExceptionNoLock.Service.testMethod(Service.java:18)
at com.leolee.multithreadProgramming.test.throwExceptionNoLock.ThreadA.run(ThreadA.java:21)
结果表明当前线程出现运行时异常的时候,并不影响其他线程执行,因为当前线程在出现异常的时候释放了对象锁。
锁不具有继承性
package com.leolee.multithreadProgramming.test.sysNotExtentds;
/**
* @ClassName Main
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Main {
public synchronized void serviceMethod() {
try {
System.out.println("main sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("main sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.leolee.multithreadProgramming.test.sysNotExtentds;
/**
* @ClassName Sub
* @Description:
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Sub extends Main {
@Override
public void serviceMethod() {
try {
System.out.println("sub sleep begin threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
Thread.sleep(5000);
System.out.println("sub sleep end threadName=" + Thread.currentThread().getName() + " time=" + System.currentTimeMillis());
super.serviceMethod();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
package com.leolee.multithreadProgramming.test.sysNotExtentds;
/**
* @ClassName ThreadA
* @Description: TODO
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class ThreadA extends Thread {
private Sub sub;
public ThreadA(Sub sub) {
super();
this.sub = sub;
}
@Override
public void run() {
sub.serviceMethod();
}
}
package com.leolee.multithreadProgramming.test.sysNotExtentds;
/**
* @ClassName ThreadB
* @Description: TODO
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class ThreadB extends Thread {
private Sub sub;
public ThreadB(Sub sub) {
super();
this.sub = sub;
}
@Override
public void run() {
sub.serviceMethod();
}
}
package com.leolee.multithreadProgramming.test.sysNotExtentds;
/**
* @ClassName Run
* @Description: 同步不具有继承性
* @Author LeoLee
* @Date 2020/8/27
* @Version V1.0
**/
public class Run {
public static void main(String[] args) {
Sub sub = new Sub();
ThreadA a = new ThreadA(sub);
a.setName("a");
a.start();
ThreadB b = new ThreadB(sub);
b.setName("b");
b.start();
}
}
执行结果:
sub sleep begin threadName=a time=1598517438110
sub sleep begin threadName=b time=1598517438117
sub sleep end threadName=b time=1598517443122
sub sleep end threadName=a time=1598517443122
main sleep begin threadName=b time=1598517443122
main sleep end threadName=b time=1598517448135
main sleep begin threadName=a time=1598517448135
main sleep end threadName=a time=1598517453139
即使Main.serviceMethod()是同步方法,但是由于Sub.serviceMethod()并不是同步方法,导致输出结果中前两行的结果,由于线程b先调用了Main.serviceMethod()同步方法,所以先获得对象锁,所以b线程先执行完毕。