今天看到一道需要动些脑子的线程面试题。

当一个线程进入一个对象的一个synchronized方法后,其它线程是否可进入此对象的其它方法?

这题一眼望去很难得到正确的答案。

标准答案也很绕。

那么干脆设计个实验试一下。

我们先建立一个对象。

public class playclass {

    //方法1:用来阻塞线程
    public synchronized void plays() throws InterruptedException {
        System.out.println("进入阻塞");
        Thread.sleep(5000);
        System.out.println("阻塞释放");
    }
   //方法2:测试synchronized方法的情况下另一个线程能否进入
    public synchronized void play2() throws InterruptedException {
        System.out.println("锁方法");
    }
   //方法3:测试synchronized类的字节码的情况下另一个线程能否进入
    public static void play3() throws InterruptedException {
        synchronized (playclass.class){
            System.out.println("锁字节码");
        }
    }
     //方法4:测试synchronized this对象的情况下另一个线程能否进入
    public void play4() throws InterruptedException {
        synchronized (this){
            System.out.println("锁this");
        }
    }
    //方法5:测试普通方法下另一个线程能否进入
    public static void play5() throws InterruptedException {
        System.out.println("普通方法");
    }
}

上面设计了五种情况,分别进行测试,通过如下测试类

public class play1 {
    public static void main(String[] args) {
        //创建对象
        playclass pc  = new playclass();
        //创建线程,分别执行所有方法,其中t1会被阻塞,检查在此情况下,其他线程是否能够进入方法
        Thread t1 = new Thread(() -> {
            try {
                pc.plays();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                pc.play2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t3 = new Thread(() -> {
            try {
                pc.play3();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t4 = new Thread(() -> {
            try {
                pc.play4();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t5 = new Thread(() -> {
            try {
                pc.play5();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        t2.start();
        t3.start();
        t4.start();
        t5.start();
    }
}

运行看结果。

首先显示的是

进入阻塞
锁字节码
普通方法

三者同时出现,五秒后显示

阻塞释放
锁this
锁方法

根据实验现象,可以得出以下结论:

在一个线程进入一个对象的一个synchronized方法并被阻塞后,如果其他线程执行该对象的普通不加锁方法或者是以字节码加锁的方法,将不会被阻塞。

如果其他对象执行synchronized方法锁的方法以及this加锁(也就是对该对象上锁)的方法时,会被阻塞,直到第一个线程被释放后,才会继续执行那些方法。

那么如果在线程被wait的情况下,会不会继续阻塞呢?

我们把方法一改为如下:

public synchronized void plays() throws InterruptedException {
        System.out.println("进入阻塞");
        this.wait();
    }

运行,可以看到所有输出同时打印。

进入阻塞
普通方法
锁字节码
锁this
锁方法

这说明如果synchronized方法中存在wait,锁会释放,无论该对象的什么方法都可以执行。

那么总结就是一句话,只有方法中没有wait且锁的对象为this(包括synchronized this与方法上加synchronized)时,该方法不可被调用,其他情况下都可以正常运行。

以上便是本题的解答。