JCIP阅读基础知识(2、3章)随笔

来源:互联网 发布:it教育哪家好 编辑:程序博客网 时间:2024/05/17 09:31

无状态对象一定是线程安全的

竞态条件:按照线程由于不恰当时序出现不正确的结果(常见情况:延迟初始化[count++]…)(先检查后执行)(区分数据竞争)

要保持状态的一致性,并非所有状态变量都使用atomic类就可以保持,需要在单个原子操作中更新所有相关的状态变量,(复合操作)

java.util.concurrent.atomic包中包含一些原子变量类,用于实现数值和对象引用上的原子状态转换,例如:AtomicLong可代替Long

Atomic类通过CAS + volatile可以比synchronized做的更高效,推荐使用

volatile实现共享变量内存可见性有一个条件,就是对共享变量的操作必须具有原子性。比如 num = 10; 这个操作具有原子性,但是 num++ 或者num--由3步组成,并不具有原子性,所以是不行的。

理解重入的概念

在使用了synchronized方法后避免使用atomic类型原子变量,使用两种不同的同步机制不仅会带来混乱,也不会在性能或安全性上带来任何好处

当执行时间较长的计算或者可能无法快速完成的操作时(例,网络I/O或控制台I/O),一定不要持有锁

内存可见性:synchronized可以实现当一个线程修改了对象状态后,其他线程能够看到发生的状态变化(同时修改)

最低安全性指,当线程没有同步的情况下读取变量时,可能会得到一个失效值,但至少这个值是由之前某个线程设置的值,而不是一个随机值。

最低安全性适用于绝大多数变量,但是存在一个例外:非volatile类型的64位数值变量(double和long),jvm允许将64位读或写操作分解为两个32位的操作,容易导致高32位与低32位混乱,解决方法:用关键字volatile声明或加锁

volatile变量通常用做某个操作完成、发生中断或者状态的标志(flag),尽管volatile变量也可以用于表示其他的状态信息,但在使用时要非常小心。

加锁机制既可以确保可见性又可以确保原子性,而volatile变量只能确保可见性

使用volatile变量要满足的条件

  • 对变量的写入操作不依赖变量的当前值,或者你能确保仅仅有单个线程更新变量的值
  • 该变量不会与其它状态变量一起纳入不变形条件中
  • 在访问变量时不需要加锁

发布将对象发布出去,使其他对象或者线程能访问到,比如存储到一个static的静态引用,或者使用其他方式使各线程能够访问到即可

如果在构造方法未初始化完毕的时候,这时候有其他线程访问就会出现问题,造成逸出

逸出:当对象未创建完成时发布对象,比如在构造函数中发布对象,即使是构造函数的最后一行发布,该对象仍旧处于未创建完成状态

线程封闭:仅在单线程内访问数据,不需要同步,实现线程安全性的最简单方式之一(Swing,JDBC)(帮助维持线程封闭性:ThreadLocal类)

Ad-hoc线程封闭:维护线程封闭性的职责完全由程序实现承担(非常脆弱尽量少使用,在可能的情况下,使用更强的线程封闭技术:栈封闭、ThreadLocal类)

当决定使用线程封闭技术时,通常是因为要将某个特定的子系统实现为一个单线程子系统(另一个原因是避免死锁,GUI框架大多数是单线程)

栈封闭中,只能通过局部变量才能访问对象,(也被称为线程内部使用或者线程局部使用)比Ad-hoc 线程封闭更易于维护,也更加健壮

ThreadLocal通常用于防止对可变的单实例对象或全局变量进行共享,ThreadLocal变量类似于全局变量,它能降低代码的可重用性,引入隐含的耦合性

不可变对象一定是线程安全的

final对象,本身不可改变,可是final中的变量是引用变量,其内容可以改变,如果是基本数据类型,初始化后不可改变

final域能确保初始化过程的安全性。从而可以不受限制地访问不可变对象,并在共享这些对象时无须同步

要安全地发布一个对象,可以通过以下方式:

  • 静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型的域或者AtomicReference对象中
  • 将对象的引用保存到某个正确的构造对象的final类型域中
  • 将对象的引用保存到一个由锁保护的域中
在并发程序中使用和共享对象是,可以使用一些实用的策略,包括:

  • 线程封闭
  • 只读共享
  • 线程安全共享
  • 保护对象

原创粉丝点击