【牛客有书共读】《Java并发编程实战》第十六章

第十六章 Java内存模型

  • 本章避开了JMM(Java内存模型)的底层细节,重点放在高层设计问题,比如安全发布,同步策略的规范以及一致性。

16.1 什么是内存模型,为什么需要它

  • Java语言规范要求JVM在线程中维护一种类似串行的语义:只要而程序的最终结果与严格串行环境中执行的结果相同,那么上述所有操作都是允许的。
  • JMM规定了JVM必须醉熏一组最小保证,这组保证规定了对遍历的写入操作在合适将对其他线程可见。

16.1.1 平台的内存模型

  • 在共享内存的多处理器体系架构中,每个处理器都拥有自己的缓存,并且定期与主存进行协调。
  • 在现代支持共享内存的多处理器中,当跨线程共享数据时,会出现一些奇怪的情况,除非通过使用内存栅栏来防止。Java程序不需要指定内存栅栏的位置,只需要通过正确地使用同步来找出合适将访问共享状态。

16.1.2 重排序

  • 没有充分同步的程序中,如果调度器采用不恰当的方式来交替执行不同线程的操作,那么僵导致不正确的结果。
  • JMM使得不同线程看到的操作执行顺序是不同的,从而导致在缺乏同步的情况下腿短操作的执行书序将变得更加复杂。各种使操作延迟或者看似乱序执行的不同原因,都可以归为重排序。

16.1.3 Java内存模型简介

  • JMM是通过各种操作来定义的,包括对变量的读/写操作,监视器的加锁和释放操作,以及线程的启动和合并操作。
  • JMM为程序中所有操作定义了一个偏序关系,成为Happens-Before,要想保证操作B的线程看到操作A的结果,那么A和B之间必须满足Happens-Before关系。
  • 如果两个操作之间缺乏Happens-Before,那么JVM可以对它们任意地重排序。

16.1.4 借助同步

  • Happens-Before的排序功能很强大,有时候可以“借助(piggyback)”现有同步截止的可见性属性。这需要将Happens-Before的程序顺序规则与其他某个顺序规则结合起来,从而对某个未被保护的变量的访问操作进行排序。
  • 在FutureTask的保护方法AbstractQueuedSynchronizer中说明了如何使用这种“借助”技术。
  • J.U.C中其他类也提供了Happens-Before排序,比如BlockingQueue, CountDownLatch, Semaphore, Future, Executor, CyclicBarrier等。

16.2 发布

16.2.1 不安全的发布

  • 除了不可变对象以外,使用被另一个线程初始化的对象通常都是不安全的,除非对象的发布操作是在使用该对象的线程开始使用之前执行。

16.2.2 安全的发布

  • Happens-Before排序是在内存访问级别上操作的,是一种“并发级汇编语言”,而安全发布的运行级别更接近程序设计。

16.2.3 安全初始化模式

  • 初始化方法生命为synchronized,可以修复UnsafeLzayInitialization中的问题,如果没有被多个线程频繁调动,可以提供满意的性能。
  • 提前初始化(Eager Initialization)避免每次加载时产生的同步开销;和JVM的延迟加载结合为延迟初始化占位(Holder)类模式,使用一个专门的类来初始化Resource。
//提前初始化
public class EagerInitialization{
	private static Resource resource = new Resource();
    pubic static Resource getResource(){return resource;}
}

//延长初始化占位类模式
public class ResourceFactory{
	private static class ResourceHolder{
		public static Resource resource = new Resource();
	}
	public static Resource getResource(){
		return ResourceHolder.resource;
	}
}

16.2.4 双重检查加锁(DCL)

  • 类似于双重校验锁的单例模式,此处放上单例模式的写法,不推荐使用。
public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}
  • 存在巨大的性能开销,已经被广泛废弃。

16.3 初始化过程中的安全性

  • 初始化安全性将确保,对于被正确构造的对象,所有线程都能看到由构造函数为对象给哥哥final域设置的正确性,而不管采用何种方式来发布对象。而且,对于可以通过被正确构造对象中某个final域达到的任意变量将同样对于其他线程是可见的。
  • 初始化安全性只能保证通过final域可达的值从构造过程完成时开始的可见性,对于通过非final域可达的值,或者在构造过程中完成后可能改变的值,必须采用同步来确保可见性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值