02 在32位的机器上对 long 型变量进行加减操作存在并发隐患?

由于 long 和 double 在 Java 中是64bit,因此:

  • 写非 volatile 修饰或加锁的 long 和 double ,在32位 CPU 上不是原子操作,在64位 CPU 上是原子操作;
  • 写 volatile 修饰或加锁的 long 和 double,无论在32还是64位的CPU上都是原子操作;

而引用类型(不管实现是32bit还是64bit),无论在32还是64位的CPU上都是原子操作;

Java 官方文档:https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.7
17.7. Non-Atomic Treatment of double and long
For the purposes of the Java programming language memory model, a single write to a non-volatile long or double value is treated as two separate writes: one to each 32-bit half. This can result in a situation where a thread sees the first 32 bits of a 64- bit value from one write, and the second 32 bits from another write.
Writes and reads of volatile long and double values are always atomic.
Writes to and reads of references are always atomic, regardless of whether they are implemented as 32-bit or 64-bit values.
Some implementations may find it convenient to divide a single write action on a 64-bit long or double value into two write actions on adjacent 32-bit values. For efficiency’s sake, this behavior is implementation-specific; an implementation of the Java Virtual Machine is free to perform writes to long and double values atomically or in two parts.
Implementations of the Java Virtual Machine are encouraged to avoid splitting 64-bit
values where possible. Programmers are encouraged to declare shared 64-bit values
as volatile or synchronize their programs correctly to avoid possible complications.

03 下面的代码用synchronized修饰代码块来尝试解决并发问题是否正确?

  1. class SafeCalc {
  2. long value = 0L;
  3. long get() {
  4. synchronized (new Object()) {
  5. return value;
  6. }
  7. }
  8. void addOne() {
  9. synchronized (new Object()) {
  10. value += 1;
  11. }
  12. }
  13. }

共享变量 value 仍然有并发安全问题,因为每次加锁的都是一个新对象,无法保证可见性和原子性;

04 String、Long 和 Integer 以及其他包装类对象适合做锁么?

不适合,首先包装类容易发生变化不适合做锁,其次Integer,Long等部分包装类实现了缓存,而 String 存在常量池,这就意味这锁可能被共用,存在风险;

06 wait()方法和sleep()方法都能让当前线程挂起一段时间,那它们的区别是什么?

wait 与 sleep 区别在于:

  1. wait 会释放锁和让出 CPU 资源,而 sleep 不会释放锁但会让出 CPU 资源(最主要)
  2. 无参的 wait() 方法被调用后,线程不会自动苏醒,需要别的线程调用同一个对象上的 notify() 或 notifyAll() 方法。sleep() 方法执行完成后,线程会自动苏醒。或可以使用 wait(long timeout) 超时后线程会自动苏醒。
  3. wait 只能在同步方法和同步块中使用,而 sleep 任何地方都可以;

两者相同点:都会让暂停线程的执行;
课后思考汇总 - 图1