在证明synchronized内建锁的可重入性和互斥性之前,我们需要了解一下synchronized如何实现线程同步问题,详情可参见https://blog.csdn.net/ty6693/article/details/89307414这篇文章。
一、证明锁的可重入性
锁的可重入性:同一个线程再次获得锁时可以获取成功而其他线程获取锁会阻塞(同一个线程重复获取同一把锁)
分析:
要证明锁的可重入性,就要验证当一个线程取得某个对象的对象锁(对象的监视器monitor)之后,即此时执行了该对象的一个同步方法,此时如果此同步方法中包含了该对象的另外一个同步方法,该线程是否可以继续执行,如果可以执行的话,证明该线程可以再次获取该对象锁,即证明了锁的可重入性。
class MyThreadX implements Runnable {
@Override
public void run() {
test1();
}
private synchronized void test2() {
System.out.println(Thread.currentThread().getName() + "进入test2方法");
}
private synchronized void test1() {
if (Thread.currentThread().getName().equals("A")){
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("A进入test1方法");
test2();
}
}
}
public class Test12 {
public static void main(String[] args) {
MyThreadX mt = new MyThreadX();
Thread thread = new Thread(mt, "A");
thread.start();
}
}
执行结果:
上述结果表明,当线程在执行一个对象的某一个同步方法时,如果这个同步方法中还调用了该对象的其它同步方法,则该线程可以继续执行这个同步方法,即证明了锁的可重入性。
二、证明锁的互斥性
分析:要证明锁的互斥,我们可以启动两个线程分别访问某对象的两个同步方法,为了可以更好的观察结果,我们可以给其中一个同步方法中写一个死循环。此时如果两个对象分别能进入到该对象的两个同步方法,说明锁不是互斥的;反之说明锁是互斥的。
class MyThreadX implements Runnable{
@Override
public void run() {
test1();
test2();
}
private synchronized void test2() {
if(Thread.currentThread().getName().equals("B")){
System.out.println("B进入test2方法");
}
}
private synchronized void test1() {
if(Thread.currentThread().getName().equals("A")){
while (true){}
}
}
}
public class Test12 {
public static void main(String[] args) {
MyThreadX mt=new MyThreadX();
Thread thread=new Thread(mt,"A");
Thread thread1=new Thread(mt,"B");
thread.start();
thread1.start();
}
}
执行结果:
观察打印结果我们可以发现,由于A线程执行的该对象的test1方法中是一个无任何内容的空结构体,此时结果中也没有任何内容,说明B线程并没有执行该对象的test2方法【如果执行了的话会打印东西】,则说明B线程并没有获取到该对象的对象锁,即证明了锁的互斥性。
此时要是调试程序,我们可以看到如下结果:
通过调试我们可以看到此时A线程的状态时执行状态,而B线程由于执行monitorenter时发现monitor计数器不为0,所以正处于获取对象锁的状态,也证明了锁的互斥性。