「每天一个知识点」一道java线程面试题分析及思考

点击上方"java全栈技术"关注,每天学习一个java知识点

「每天一个知识点」一道java线程面试题分析及思考

「每天一个知识点」一道java线程面试题分析及思考

该程序的输出结果?

「每天一个知识点」一道java线程面试题分析及思考

程序输出结果

main thread b=1000b=1000或main thread b=2000b=1000

考察知识点

  • synchronized。
  • 并发下的内存可见性。
  • Thread.sleep

分析

synchronized的作用就是实现线程的同步(实现线程同步方法之一)。它的工作就是对需要同步的代码加锁,使得每一次只有一个线程可以进入同步块(其实是一种悲观策略),从而保证线程执行的安全性。

一般关键词synchronized的用法

  • 指定加锁对象。对给定对象加锁,进入同步代码前需要灵活的给定对象的锁。
  • 作用于实例方法。相当于对当前实例加锁,进入同步代码前要获得当前实例的锁。
  • 作用于静态方法。相当于对当前类加锁,进入同步代码前要获得当前类的锁。

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

Thread.sleep

使当前线程(即调用该方法的线程)暂停执行一段时间,让其他线程有机会继续执行,但它并不释放对象锁。也就是说如果有synchronized同步块,其他线程仍然不能访问共享数据。注意该方法要捕捉异常,对于后面的分析很有作用。

流程分析

该题涉及到2个线程(子线程、主线程main)。对于属性变量 int b =100由于使用了synchronized也不会存在可见性问题(没有必要再使用volatile)。

从main方法开始执行,当执行步骤1的时候

Thread t = new Thread(tt); //1

线程是new状态,还没有开始工作。

当执行步骤2的时候

t.start(); //2

调用start方法,这个线程才被真正启动,进入runnable状态。runnable状态表示一切准备就绪,可以执行。但还并不表示立即会在cpu上执行,真正执行取决服务cpu的调度

在这里当执行步骤3(tt.m2();)必定是先获得锁(由于start需要调用native方法,并且在用完成之后在一切准备就绪了,但是并不表示一定在cpu上面执行,有没有真正执行取决服务cpu的调度,之后才会调用run方法,执行m1方法)。

其实这里的两个synchronized方法里面的Thread.sheep是没有影响的,为了混淆增加难度。

步骤3执行的时候其实很快子线程也准备好了,但是由于synchronized的存在,并且是作用同一对象,所以子线程就只有必须等待了。

由于main方法里面顺序执行的,所以必须是步骤3执行完成之后才可以到步骤4,而由于步骤3执行完成,子线程就可以执行m1了。

这里就存在一个多线程谁先获取到锁的问题。

如果步骤3先获取那么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哪些是线程安全不安全,那样使用的时候不会出现莫名其妙的问题。


分享到:


相關文章: