史上最难的一道Java面试题 (分析篇)

标签: java 面试题 并发
32963人阅读 评论(97) 收藏 举报
分类:

转载请注明原创出处,谢谢!

无意中了解到如下题目,觉得蛮好。

题目如下:

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
或
main thread b=1000
b=1000

考察知识点

  • synchronize实例锁。
  • 并发下的内存可见性。

在java中,多线程的程序最难理解、调试,很多时候执行结果并不像我们想象的那样执行。所以在java多线程特别难,依稀记得大学的时候考c语言二级的时候,里面的题目是什么++和很多其他优先级的符合在一起问最后的输出结果,这类题目就想考一些运行符优先级和结合性问题。那个背背就行了,但是java多线程还是需要好好理解才行,靠背是不行的。

下面开始简单分析:

该题目涉及到2个线程(主线程main、子线程)、关键词涉及到synchronized、Thread.sleep。
synchronized关键词还是比较复杂的(可能有时候没有理解到位所以上面题目会有点误区),他的作用就是实现线程的同步(实现线程同步有很多方法,它只是一种后续文章会说其他的,需要好好研究大神Doug Lea的一些实现),它的工作就是对需要同步的代码加锁,使得每一次只有一个线程可以进入同步块(其实是一种悲观策略)从而保证线程只记得安全性。

一般关键词synchronized的用法
  • 指定加锁对象:对给定对象加锁,进入同步代码前需要活的给定对象的锁。
  • 直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
  • 直接作用于静态方法:相当于对当前类加锁,进入同步代码前要获得当前类的锁。

上面的代码,synchronized用法其实就 属于第二种情况。直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。

可能存在的误区
  1. 由于对synchronized理解的不到为,由于很多时候,我们多线程都是操作一个synchronized的方法,当2个线程调用2个不同synchronized的方法的时候,认为是没有关系的,这种想法是存在误区的。直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
  2. 如果一个调用synchronized方法。另外一个调用普通方法是没有关系的,2个是不存在等待关系的。

这些对于后面的分析很有作用。

Thread.sleep

使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步快,其他线程仍然不能访问共享数据。注意该方法要捕捉异常,对于后面的分析很有作用。一些细节可以参考我的系统学习java高并发系列二

分析流程

java 都是从main方法执行的,上面说了有2个线程,但是这里就算修改线程优先级也没用,优先级是在2个程序都还没有执行的时候才有先后,现在这个代码一执行,主线程main已经执行了。对于属性变量 int b =100由于使用了synchronized也不会存在可见性问题(也没有必要在说使用volatile申明),当执行1步骤的时候(Thread t = new Thread(tt); //1)线程是new状态,还没有开始工作。当执行2步骤的时候(t.start(); //2)当调用start方法,这个线程才正真被启动,进入runnable状态,runnable状态表示可以执行,一切准备就绪了,但是并不表示一定在cpu上面执行,有没有真正执行取决服务cpu的调度。在这里当执行3步骤必定是先获得锁(由于start需要调用native方法,并且在用完成之后在一切准备就绪了,但是并不表示一定在cpu上面执行,有没有真正执行取决服务cpu的调度,之后才会调用run方法,执行m1方法)。这里其实2个synchronized方法里面的Thread.sheep其实要不要是无所谓的,估计是就为混淆增加难度。3步骤执行的时候其实很快子线程也准备好了,但是由于synchronized的存在,并且是作用同一对象,所以子线程就只有必须等待了。由于main方法里面执行顺序是顺序执行的,所以必须是步骤3执行完成之后才可以到4步骤,而由于3步骤执行完成,子线程就可以执行m1了。这里就存在一个多线程谁先获取到问题,如果4步骤先获取那么main thread b=2000,如果子线程m1获取到可能就b已经赋值成1000或者还没有来得及赋值4步骤就输出了可能结果就是main thread b=1000或者main thread b=2000,在这里如果把6步骤去掉那么b=执行在前和main thread b=在前就不确定了。但是由于6步骤存在,所以不管怎么都是main thread b=在前面,那么等于1000还是2000看情况,之后b=1000是一定固定的了。

多线程一些建议

  • 线程也很珍贵,所以建议使用线程池,线程池用的很多,后续准备分享下,特别重要,需要做到心中有数。
  • 给线程起名字,当线上cpu高的时候,需要用到高级jstack,如果有名称就方便很多。
  • 多线程特别需要注意线程安全问题,也需要了解jdk那些是线程安全不安全,那样使用的时候不会出现莫名其妙问题。

还有一些技巧后续文章分享在慢慢提,多线程特别重要,也特别难,希望大家也多多花心思在上面。

多线程的一些调试技巧

由于断点,所有线程经过断点的时候,都需要停下,导致这个点不停的断住,很难受,eclispe里面有条件断点,当满足条件的时候就可以停下来,那么这样就方便了。

关于线程dump分析以及后续线程内容会在后面继续分析分享。


本人其他JVM菜鸟进阶高手之路相关文章


如果读完觉得有收获的话,欢迎点赞加关注。

查看评论

C++的profile辅助模板类 升级版

(如果没看过"C++的profile辅助模板类" 先去看看那里的说明部分http://www.csdn.net/Develop/read_article.asp?id=26029 )先看例子:  PR...
  • coolcch
  • coolcch
  • 2004-03-29 10:24:00
  • 567

Java基础面试题及答案

面向对象编程(OOP) Java是支持并发,基于类的以及面向对象的一种计算机编程语言. 以下列举了面向对象编程的优势: 模块化编程,使维护和修改更加容易 代码重用 提高代码的可...
  • a3025056
  • a3025056
  • 2017-02-09 09:23:47
  • 6621

java比较难面试题(一)

题目一.                58阅读以下程序,运行结果为                   public classExamTest {  publ...
  • K_122
  • K_122
  • 2017-08-02 14:33:52
  • 962

稍微有点难度的10道java面试题,你会几道?

  • 2017年08月22日 11:13
  • 796B
  • 下载

有了这些java面试题目和答案,你还有什么过不去的梗

Java开发学习 2017-04-27 09:58 2017年面试官常问的Java问题 1.什么是Java虚拟机?为什么Java被称作是“平台无关的编程语言”? Java虚拟机是一个可以执行Ja...
  • u011277123
  • u011277123
  • 2017-04-28 11:19:57
  • 2129

最有价值的50道java面试题 适用于准入职Java程序员

下面的内容是对网上原有的Java面试题集及答案进行了全面修订之后给出的负责任的题目和答案,原来的题目中有很多重复题目和无价值的题目,还有不少的参考答案也是错误的,修改后的Java面试题集参照了JDK最...
  • qq_35114086
  • qq_35114086
  • 2016-11-28 19:37:03
  • 5536

java 基础试题及答案(自己的理解)

1、给定如下JAVA 程序片断: class A{ public A(){ system.out.println(“A”); } } class B extends A{ public B(){Sys...
  • hwctl
  • hwctl
  • 2007-04-26 10:29:00
  • 7975

Java经典逻辑编程50题

相信很多 Java 初学者都看到过这些题,本人也看了几遍,最终下定决心弄到我的博客中来作为我的学习记录,同时也可供后面的 Java 学习者参考。题目的代码有些时看别人的,大部分是自己写的,如有雷同,请...
  • Alias_fa
  • Alias_fa
  • 2016-10-31 19:10:15
  • 6725

JAVA经典试题四十道

一个月前看到了JAVA经典试题五十道,很感兴趣,也当做是机会提升自己的能力,本想着一个周做完,但是计划太唐突,根本想不到做这些题目这么需要花时间,而且基本都是无聊的数组数组数组!!!就靠你的纯逻辑逻辑...
  • sakura_yuan
  • sakura_yuan
  • 2016-06-15 00:13:49
  • 16070

成为Java顶尖程序员 ,看这11本书就够了

“学习的最好途径就是看书“,这是我自己学习并且小有了一定的积累之后的第一体会。个人认为看书有两点好处: 1.能出版出来的书一定是经过反复的思考、雕琢和审核的,因此从专业性的角度来说,一本...
  • wuzhoudao
  • wuzhoudao
  • 2016-12-15 15:26:42
  • 6397
    统计
    个人资料
    专栏达人 持之以恒
    等级:
    访问量: 37万+
    积分: 3452
    排名: 1万+
    博客专栏