天天看点

一道很有意思的java线程题

这几天看结城浩的《java多线程设计模式》,跟着做一些习题,有几道题目很有意思,记录下自己的体会。

  首先是题目(在原书212页,书尾有解答):

public class Main {
    public static void main(String[] args) {
        try {
            Blackhole.enter(new Object());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}      
public class Blackhole {    
        public static void enter(Object obj) throws InterruptedException {
        System.out.println("1");
        magic(obj);
        System.out.println("2");
        synchronized (obj) {
            System.out.println("3");
        }
    }

    private static void magic(Object obj){}
}      

 代码很简单,要求写出magic()代码,使得输出是

1
2      

不能出现3.

思路:很明显要想不输出3,magic()必须得到obj锁,且不能释放才行。

private static void magic(final Object obj) throws InterruptedException {
        Thread thread = new Thread() {
            @Override
            public void run() {
                synchronized (obj) {
                    synchronized (this) {
                        this.setName("LockNow");
                        this.notifyAll();
                    }
                    while (true) {
                    }
                }
            }
        };
        synchronized (thread) {
            thread.setName("");
            thread.start();
            while (thread.getName().equals("")) {
                thread.wait();
            }
        }
    }      

 作者的思路很巧妙,通过thread.name的值来处理2个线程的执行次序。

   1. 创建一个内部Thread实例thread,先不start()

   2. 然后由主线程获得thread锁,并启动thread线程,然后开始等待。

   3. thread线程会去获得obj锁,获得obj锁之后,该线程会修改自己name,并通知主线程。

   4. 主线程发现条件满足,继续执行

   刚看到答案,有个很大疑问,主线程获得thread锁之后,启动thread线程,而thread线程为了修改name,必须获得自己的锁(否则运行时会报错java.lang.IllegalMonitorStateException),这不死锁了吗?

   仔细一想又不会,因为新线程开启之后,如果新线程运行到synchronized (this)被阻挡而无法修改name,主线程肯定会进入wait,而wait时主线程释放thread锁,新线程就可继续往下跑。

  短短几行代码,线程之间同步协调环环相扣,不得不佩服作者的功力!