第3章 对象的共享

Synchronized关键字可以用于实现:

1.原子性

2.确定临界区

3.内存可见性,一个线程修改了状态,其他等待线程可以立即感知。

可见性:

1.失效数据 线程在check之后act前,数据被另外的线程修改。

2.非原子64位操作 java虚拟机模型,允许将long double等64位的数据,分成高32位和低32位进行操作。可能当计算完高32位后,数据变化,低32位随之变化,之前计算的高32位失效。

3.加锁与可见性 加锁的意义不仅在于互斥,还能确保所有线程在得到锁后,都能看见最新的值。

4.volatile 只保证可见性,访问volatile变量时不会加锁,编译时不会将该变量上的操作,与其他内存操作一起进行重排序。通常用作线程间通信的条件标志。

一般变量:优先读取工作内存,若不存在,则总从主内存中copy一份到工作内存,读写操作都只修改工作内存;

volatile修饰的变量:当一个线程修改了变量的值,新的值会立刻同步到主内存当中。而其他线程读取这个变量的时候,也会从主内存中拉取最新的变量值。

使用volatile的条件:

1.对变量的写入操作不依赖当前值(例如自增操作,不保证原子性)

2.该变量不会与其他状态变量一起纳入不变性条件(volatile变量肯定会变化,不具备不变性)

3.在访问变量时,无需加锁

不变性:某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象,不可变对象一定是线程安全的。不可变对象很简单。他们只有一种状态,并且该状态由构造函数来控制。

当满足以下条件时,对象才是不可变的:(1)、对象创建以后其状态就不能改变;(2)、对象的所有域都是final类型;(3)、对象是正确创造的(在对象创建期间,this引用没有溢出)。

发布:通过外部方法(其他类定义的方法,或者非final、private方法)一个对象被传到作用域之外。

逸出:不正确的发布。

this引用逸出是怎样产生的?

它需要满足两个条件:一个是在构造函数中创建内部类(EventListener),另一个是在构造函数中就把这个内部类给发布了出去(source.registerListener)。内部类、匿名内部类都可以访问外部类的对象的域,为什么会这样,实际上是因为内部类构造的时候,会把外部类的对象this隐式的作为一个参数传递给内部类的构造方法,这个工作是编译器做的,它会给内部类所有的构造方法添加这个参数,所以例子里的匿名内部类在构造ThisEscape时就把ThisEscape创建的对象隐式的传给匿名内部类了。

线程封闭:仅在单个线程中访问数据,将可变对象封闭在一个线程里面,自动实现线程安全。

java提供了局部变量和thread local来维持线程封闭,但在开发时仍要注意不要让封闭的对象逸出。

ad-hoc线程封闭:维护线程封闭的职责完全由程序来承担,过于脆弱,不建议使用。

栈封闭:局部变量被封闭在线程栈中,其他线程无法访问,通过局部变量才能访问数据对象,即为栈封闭。  缺点:需要明确哪些局部变量的引用不能逸出,后期维护可能导致逸出。

TreadLocal类:用于防止对可变的单实例变量或全局变量进行共享,提供get set方法,为每次使用该变量的线程返回一个独立的副本。每个线程的变量副本存放在thread中,线程消亡后进行垃圾回收。

4.不变性(final-不变性,volatile-可见性)

不可变对象:对象创建后,状态不能被修改;所有域都是final的;对象被正确创建(没有this逸出)。

性质:一定是线程安全的。

4.1 final:当指向primitive类型或者string时,可以保证不可变性;当指向对象时,仅保证指向的地址空间不变,但是对象的属性是否可变不受该final限制。

4.2 volatile:可变对象需要锁来确保线程安全;不可变对象无法被修改,线程需要更新状态,可以对其副本进行修改,然后将volatile的引用指向新对象,这样可以在不加锁的情况下保证线程安全。

5.3 安全发布的常用模式

安全发布:当对象的引用对所有访问该对象的线程可见时,对象发布时的状态对所有线程也是可见的。

1)在静态初始化函数中初始化一个对象引用(静态初始化由JVM在类初始化阶段完成,JVM内部存在同步机制,可以保障对象被安全的发布)

2)将对象的引用保存在volatile域或AtomicReferance对象中。

3)将对象的引用保存到某个正确构造对象的final类型域中。

4)将对象的引用保存到一个由锁保护的域中(将对象放入线程安全的容器中,如Hashtable, synchronizedMap, ConcurrentMap; vector, CopyOnWriteArrayList, CopyOnWriteArraySet, SynchronizedList, SynchronizedSet; BlockingQueue, ConcurrenLinkedQueue)。

5.4 事实不可变对象:技术上,对于发布的对象能够进行修改,但发布后却不会进行修改的对象。将其看作不可变对象,减少同步,提高效率。

5.5

不可变对象可以通过任意机制发布;

事实不可变对象必须通过安全的方式发布;

可变对象必须通过安全的方式来发布,并且是线程安全的或者由锁来保护。

5.6 安全地共享对象

线程封闭

只读共享:不可变、事实不可变对象

线程安全共享:线程安全的对象在其内部实现同步,线程通过对象的公有接口进行访问,无需额外同步。

保护对象:被保护的对象只能通过持有锁来访问。保护对象包括封装在其他线程安全对象中的对象,以及已经发布的由某个特定锁保护的对象。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容