文章目录
1、volatile 修饰符的有过什么实践?
一种实践是用 volatile 修饰 long 和 double 变量,使其能按原子类型来读写。 double 和 long 都是 64位宽,因此对这两种类型的读写分为两部分的,第一次读取第一个32位,然后再读剩下的32位,这个过程不是原子的,但 Java 中 volatile 型的 long 或 double 变量的读写是原子的。 volatile 修饰符的另一个作用是提供内存屏障(memory barrier),例如在分布式框架中的应用。简单的说,就是当你写一个 volatile 变量之前,Java 内存模型会插入一个写 屏障(writer barrier),读一个 volatile 变量之前,会插入一个读屏障(read barrier)。意思就是说,在你写一个 volatile 域时,能保证任何线程都能看到你写的值,同时,写之前,也能保证任何数值的更新对所有线程是可见的,因为内存屏障会将其他所有写的值更新到缓存。
2、 volatile 类型变量提供了什么保证?
volatile 变量提供顺序和可见性保证,例如, JVM 或者 JIT为了获得更好的性能会对语句重排序,但是 volatile 类型变量即使在没有同步块的的情况下赋值也不会与其他语句重排序。volatile 提供 happens-before 的保证,确保一个线程的修改能对其他线程是可见的。某些情况下volatile 还能提供原子性,如读 64位数据类型,像 long 和 double 都不是原子的,但 volatile类型的 double 和 long 就是原子的。
3、10 个线程和 2个线程的同步代码,哪个更容易写?
从写代码的角度来说,两者的复杂度是相同的,因为同步代码与线程数量相互独立的。但是同步策略的选择依赖于线程的数量,因为越多的线程意味着更大的竞争,所以你需要利用同步技术,如锁分离,这要求复杂的代码和专业知识。
4、你是如何调用 wait() 方法的? 使用 if 块还是 循环? 为什么
wait() 方法应该在循环调用,因为当线程获取到 CPU 开始执行的时候,其他条件可能还没有满足,所在处理前,循环检查条件是否满足会更好。下面是一段标准的使用 wait 和 notify 方法的代码:
// The standard idiom for using the wait method
synchronized (obj){
while (condition does no hold)
obj.wait();
// (Releases lock,and reacquires on weekup)
...// Perform action appropriate to codition
}
5、什么是多线程环境下的伪共享(false sharing)?
伪共享是多线程系统(每个处理器有自己的局部缓存)中一个众所周知的性能问题。伪共享发生在不同处理器上的线程对变量的修改依赖于相同的缓存行。