更新时间:
#一道“史上最难”java面试题引发的线程安全思考
最近偶然间看见一道名为史上最难的java面试题,这个题让了我对线程安全的有了一些新的思考,给大家分享一下这个题吧:
public class TestSync2 implements Runnable {
int b = 100;
synchronized void m1() throws InterruptedException {
b = 1000;
Thread.sleep(500); //6
System.out.println("b=" + b);
}
synchronized void m2() throws InterruptedException {
Thread.sleep(250); //5
b = 2000;
}
public static void main(String[] args) throws InterruptedException {
TestSync2 tt = new TestSync2();
Thread t = new Thread(tt); //1
t.start(); //2
tt.m2(); //3
System.out.println("main thread b=" + tt.b); //4
}
@Override
public void run() {
try {
m1();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
总而言之问了很多人,大部分第一时间都不能得出正确答案,其实正确答案如下:
main thread b=2000
b=1000
or
main thread b=1000
b=1000
这个题涉及了两个点:
- synchronized
- 线程的几个状态:new,runnable(thread.start()),running,blocking(Thread.Sleep())
如果对这几个不熟悉的同学不要着急下面我都会讲,下面我解释一下整个流程:
- 新建一个线程t, 此时线程t为new状态。
- 调用t.start(),将线程至于runnable状态。
- 这里有个争议点到点是t线程先执行还是tt.m2先执行呢,我们知道此时线程t还是runnable状态,此时还没有被cpu调度,但是我们的tt.m2()是我们本地的方法代码,此时一定是tt.m2()先执行。
- 执行tt.m2()进入synchronized同步代码块,开始执行代码,这里的sleep()没啥用就是混淆大家视野的,此时b=2000。
- 在执行tt.m2()的时候。有两个情况:
情况A:有可能t线程已经在执行了,但是由于m2先进入了同步代码块,这个时候t进入阻塞状态,然后主线程也将会执行输出,这个时候又有一个争议到底是谁先执行?是t先执行还是主线程,这里有小伙伴就会把第3点拿出来说,肯定是先输出啊,t线程不是阻塞的吗,调度到CPU肯定来不及啊?很多人忽略了一点,synchronized其实是在1.6之后做了很多优化的,其中就有一个自旋锁,就能保证不需要让出CPU,有可能刚好这部分时间和主线程输出重合,并且在他之前就有可能发生,b先等于1000,这个时候主线程输出其实就会有两种情况。2000 或者 1000。
情况B:有可能t还没执行,tt.m2()一执行完,他刚好就执行,这个时候还是有两种情况。b=2000或者1000
6.在t线程中不论哪种情况,最后肯定会输出1000,因为此时没有修改1000的地方了。

https://juejin.im/post/5b632cbee51d45190f4af48e?utm%5Fmedium=be&utm%5Fsource=weixinqun
``
``