java并发编程实践笔记-1-3章

避免多线程错误三个方法:
(1)不共享变量(ThreadLocal);//空间换时间
(2)共享变量设定为不可变(Immutable);//空间换时间
(3)使用同步(Synchronized).//时间换空间

tips:

java8stream在parallel中,不能操作非线程安全的类。

Synchronized

内置锁.
非静态方法synchronized: 对当前对象加锁;
静态方法synchronized: 对Class对象加锁.

同步块:

1
2
3
synchronized(this){
//do something
}

因为锁是加在某个对象上,所以叫做内置锁.线程在进入同步块的时候获得锁,出来的时候释放锁.

释放锁的情况:

  1. 正常退出
  2. 异常退出.
  • 优点:
    同步块中的代码能作为一个原子操作.

  • 缺点:
    只有一个线程能获得锁.性能较低.

可重入

重入: 当某个线程试图获取它自己已经持有的锁.
Synchronized内置锁是可重入的,意味着线程可以多次获取自己持有的内置锁,都可以获得成功.

实现:

锁在对象里,锁中记录持有者的线程和获取计数器.
计数器为0时,锁被认为不被任何线程所持有;
当线程请求一个计数器为0的锁时,锁中记录下锁的持有者,并且将计数器置为1.如果再一次进入同步块,计数器+1,退出一次同步块计数器-1,当计数器为0,锁被释放.

可见性

不加任何同步控制的变量,由于指令重排,多线程等因素,可见性无法保证,读线程可能读到的不是最新的值(读到失效数据).这种级别是最低安全性.

对于大部分基本类型来说,最低安全性是可以容忍的.但对于double,long来说,由于被更新的可能是数据的一半,除非使用volatile关键字等机制,否则读出来的可能不仅是失效数据,而是一个混合了失效数据\最新数据的近乎随机的值,可能带来毁灭性的后果.

volatile

作用: 保持可见性
缺点: 不保证操作的原子性. (需要原子性,应该使用锁或原子类)
使用场景:

  1. boolean值,状态变量;
  2. 只有一个线程写,其他线程读.
    或 写的时候不依赖原先的值(不是自增这种).

编译优化与变量

jvm在server模式下对循环内没有改变的变量进行优化,将其提出循环.
;在client模式下则不会有这种优化.

示例代码:

1
2
3
4
boolean otherDone;//此处应该volatile
while(!otherDone){
//sleep
}

上述代码如果不加volatile,则在server模式下可能会死锁,因为!otherDone的判断可能会被优化到循环外面.导致无限循环.

线程封闭

不在线程间共享变量,因此可以达到线程安全.
例如使用ThreadLocal.

栈封闭

线程封闭的特例.
由于每个线程有自己的栈,而局部变量在栈上,因此只使用局部变量的话,就是栈封闭.(达到线程安全)
由于基本类型无法获取引用,因此基本类型无法逸出,是安全的;
而对象引用可能逸出,因此需要编程人员自行保证不会逸出.
(不把引用乱传递)

发布与逸出

  1. 线程安全的叫发布;
  2. 不安全的叫逸出.

安全发布的两种途径:

  1. 发布的对象是不可变的;
  2. 发布的时候使用同步方法.

途径1

其中,不可变的方法:

private final,且返回clone或Arrays.copy的值.

途径2

把不可变对象安全发布的方法:

  1. 静态初始化函数中初始化一个对象引用;
  2. 把对象引用存放到volatile或AtomicReference;
  3. 把对象引用存放到正确构造对象的final类型域;
  4. 把对象引用存放到一个由锁保护的域中.(如放入同步容器中,包括HashTable,Vector,sychronizedMap,synchronizedMap,concurrentMap,CopyOnWriteArrayList,BlockingQueue,ConcurrentLinkedQueue).
  • 方法1: 静态初始化函数
    1
    public static Holder holder=new Holder(42);

对于不可变对象,安全发布后就可以用了;
对于可变对象,安全发布以后还需要安全得使用,也就是使用的时候也需要同步.

  • 总结:

    不可变对象: 任意发布
    事实不可变: 安全发布
    可变: 安全发布且安全使用.

建议

  1. 尽量使用final,private.
    其中final能保证初始化值过程中的安全性.

疑问

  1. immutable和threadLocal选哪个?

推荐文章