当前位置: 首页 > 科技观察

史上最难的Java面试题

时间:2023-03-15 13:59:49 科技观察

无意中了解到下面这道题,觉得还不错。话题如下.sleep(250);//5b=2000;}publicstaticvoidmain(String[]args)throwsInterruptedException{TestSync2tt=newTestSync2();Threadt=newThread(tt);//1t.start();//2tt.m2();//3System.out.println("mainthreadb="+tt.b);//4}@Overridepublicvoidrun(){try{m1();}catch(InterruptedExceptione){e.printStackTrace();}}}程序结果的输出?程序输出结果mainthreadb=2000b=1000ormainthreadb=1000b=1000检查知识点同步实例锁。并发下的内存可见性。在java中,多线程程序是最难理解和调试的,很多时候执行结果并没有像我们想象的那样执行。所以,java中的多线程是特别难的。依稀记得大学时候考C语言二级,里面的题目是什么?考虑一些运算符优先级和结合性问题。备份就够了,但是java多线程还是要好好理解,备份还不够。下面开始简单分析题目,涉及2个线程(主线程main,子线程),synchronized相关的关键字,Thread.sleep。synchronized关键字还是比较复杂的(可能有时候不是很理解,所以上面的话题会有点误会),它的作用是实现线程同步(实现线程同步的方法有很多,这里只是跟贴-上篇文章会讲到其他的,需要研究大师DougLea的一些实现),它的工作是把需要同步的代码加锁,这样一次只能有一个线程进入同步块(其实是一种悲观策略)以确保线程只记住安全性。一般关键字synchronized的用法指定锁对象:给给定对象加锁,给定对象的锁在进入同步代码前需要激活。直接作用于实例方法:相当于给当前实例加锁,在进入同步代码前必须获取当前实例的锁。直接作用于静态方法:相当于给当前类加锁,在进入同步代码前获取当前类的锁。上面代码中,synchronized的用法其实属于第二种情况。直接作用于实例方法:相当于给当前实例加锁,在进入同步代码前必须获取当前实例的锁。可能产生的误解1、由于对synchronized缺乏了解,因为很多时候,我们是用多个线程来操作一个synchronized方法。当两个线程调用两个不同的synchronized方法时,就认为是无关紧要的。这种想法是错误的。直接作用于实例方法:相当于给当前实例加锁,在进入同步代码前必须获取当前实例的锁。2.如果调用了一个synchronized方法。另一个调用普通方法也无所谓,两者之间没有等待关系。这些对后面的分析很有用。Thread.sleep使当前线程(即调用该方法的线程)暂停执行一段时间,给其他线程继续执行的机会,但并不释放对象锁。也就是说,如果有一个synchronized同步快,其他线程仍然无法访问共享数据。注意这个方法需要捕获异常,这对后面的分析很有用。解析过程java是从main方法执行的。据说有2个线程,但是这里修改线程优先级是没有用的。优先级仅在2个程序未执行时。现在这段代码一执行,主线程main就已经执行完了。对于属性变量intb=100,不会因为使用了synchronized而存在可见性问题(也不需要使用volatile声明),在执行步骤1时(Threadt=newThread(tt);//1)thread是一个新的状态,还没有开始工作。当执行到2步(t.start();//2)调用start方法时,线程才真正启动,进入runnable状态。runnable状态表示可以执行,一切准备就绪,但并不一定表示在cpu上执行,是否真正执行取决于服务cpu的调度。这里,执行3步时,必须先获取锁(因为start需要调用native方法,使用完成后一切就绪,但并不代表一定要在cpu上执行,是否实际执行取决于服务cpu的调度,然后会调用run方法并执行m1方法)。其实这两个synchronized方法里有没有Thread.sheep都无所谓,估计会增加混淆的难度。当第3步执行时,子线程其实很快就准备好了,但是由于synchronized的存在,作用于同一个对象,子线程不得不等待。由于main方法中的执行顺序是顺序执行的,因此在执行完步骤3之后必须完成步骤4,而由于执行了步骤3,子线程才能执行m1。这里有一个多线程的问题,谁先得到。如果第4步先拿到,那么主线程b=2000。如果子线程m1拿到了,说明b已经赋值1000或者第4步还没来得及赋值。可能的结果是主线程b=1000或者主线程b=2000,如果去掉这里的6步,那么b=先执行,主线程b=before就不确定了。但是既然有6步,不管怎样,主线程b=在前面,所以等于1000还是2000要看情况,然后b=1000一定要固定。多线程的一些建议。线程也很宝贵,所以推荐使用线程池。线程池用的很多。稍后分享它们非常重要,您需要了解它们。给线程一个名字。在线cpu高的时候,需要使用advancedjstack。有名字就方便多了。多线程需要特别注意线程安全问题,同时也要了解jdk是线程安全的还是不安全的,这样使用的时候才不会出现莫名其妙的问题。还有一些技巧可以在后续的文章中分享。多线程尤为重要和困难。希望大家多多关注。多线程的一些调试技巧,都是靠断点来实现的。当所有的线程都经过一个断点的时候,都需要停下来,导致这个点不停的停下来,很不爽。eclispe中有条件断点,满足条件就可以停止。下来吧,那就方便了。threaddump的分析以及后续thread的内容,后面会继续分析分享。