《Java并发编程实战》-2

对象的共享

3.1 可见性

在没有同步的情况下,编辑器、处理器以及运行时等都可能对操作的执行顺序进行一些意想不到的调整。在缺乏足够同步的多线程程序中,想要对内存操作的执行顺序进行判断,几乎无法得出正确的结论。

3.1.1 失效数据

线程安全的可变整数类

@ThreadSafe
public class SynchronizedInteger {
    @GuardedBy("this") private int value;

   public synchronized int get() { return value; }
   public synchronized void set(int vale) { this.value = value; }
}

3.1.2 非原子的64位操作

在多线程环境中使用共享且可变的long和double等类型的变量时,应该使用关键字volatile来声明它们,或者用锁保护起来。

3.1.3 加锁与可见性

加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的新值,所有执行读操作或者写操作的线程必须在同一个锁上同步。

3.1.4 Volatile变量

仅当volatile变量能简化代码的实现以及对同步策略的验证时,才应该使用它们。如果在验证正确性时需要对可见性进行复杂的判断,那么就不要使用volatile变量。volatile变量的正确使用方式包括:确保它们自身状态的可见性,确保它们所引用对象的状态的可见性,以及标识一些重要的程序声明周期时间的发生(例如,初始化或关闭)。

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

当且仅当满足一下所有条件时,才应该使用volatile变量:

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

3.2 发布与逸出

安全的对象构造过程

不要再构造过程中使this引用逸出。

3.3 线程封闭

3.1 Ad-hoc线程封闭

Ad-hoc线程封闭是指,维护线程封闭的指责完成由程序实现来承担。

3.3.2 栈封闭

栈封闭是线程封闭的一种特例,在栈封闭中,只能通过局部变量才能访问对象。

3.3.3 ThreadLocal类

维持线程封闭性的一种更规范方法是使用ThreadLocal,这个类能使线程中的某个值与保存值得对象关联起来。ThreadLocal提供get与set等访问接口或方法,这些方法为每个使用该变量得线程都存有一份独立得副本,因此get总是返回由当前执行线程在调用set时设置得最新值。

使用ThreadLocal来维持线程封闭性

 prvate static ThreadLocal<Connection> connectionHolder 
 = new ThreadLocal<Connection>() {
     public Connection initalValue() {
        return DriverManager.getConnection(DB_URL); 
    }
 };

public static Connection getConnection() {
    return connectionHolder.get();
}

3.4 不变性

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

当满足一下条件时,对象才是不可变的:

  • 对象创建以后其状态就不能修改。
  • 对象的所有域都是final类型。
  • 对象是正确创建的(在对象的创建期间,this引用没有逸出)

3.4.1 Final域

正如“除非需要更高的可见性,否者应将所有的域都声明为私有域”是一个良好的编程习惯,”除非需要某个域是可变的,否则就应该将其声明为final域“也是一个良好的编程习惯。

3.4.2 实例:使用Volatile类型来发布不可变对象

3.5 安全发布

3.5.1 不正确的发布:正确的对象被破坏

3.5.2 不可变对象与初始化安全性

任何线程都可以在不需要同步的情况下安全地访问不可变对象,即使在发布这些对象时没有使用同步。

3.5.3 安全发布的常用模式

要安全地发布一个对象,对象的引用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过以下方式来安全地发布:

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型的域或者AtomicReferance对象中。
  • 将对象的引用保存到某个正确的构造对象的final类型域中。
  • 将对象的引用保存到一个由锁保护的域中。

3.5.4 事实不可变对象

在没有额外同步的情况下,任何线程都可以安全地使用被安全发布的事实不可变对象。

3.5.5 可变对象

对象的发布需求取决于它的可变性:

  • 不可变对象可以通过任意机制来发布。
  • 事实不可变对象必须通过安全方式来发布。
  • 可变对象必须通过安全方式来发布,并且必须是线程安全的或者由某个锁保护起来。

在并发程序中使用和共享对象时,可以使用一些实用的策略,包括:

  • 线程封闭 线程封闭的对象只能由一个线程拥有,对象被封闭在该线程中,并且只能由这个线程修改。
  • 只读共享 在没有额外同步的情况下,共享的只读对象可以由多个线程并发访问,但任何线程都不能修改它。共享的只读对象包括不可变对象和事实不可变对象。
  • 线程安全共享 线程安全的对象在其内部实现同步,因此多个线程可以通过对象的公有接口来进行访问而不需要进一步的同步。
  • 保护对象 被保护的对象只能通过持有特定的锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及已发布的并且由某个特定锁保护的对象。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值