《Java并发编程实践——第三章(共享对象)》

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u011067966/article/details/84069241

共享对象##

编写正确的并发程序的关键在于对共享的、可变的对象状态进行访问管理。
上一章使用同步来避免多个线程在同一时间访问同一数据。
同步还有另外的方面:内存可见性。

3.1 可见性

public class NoVisibility {
	private static boolean ready;
	private static int  number;
	private static class ReaderThread extends Thread{
		@Override
		public void run() {
			while (!ready) {
				Thread.yield();
			
			}
			System.out.println(number);
		}
	}
	public static void main(String[] args) {
		new ReaderThread().start();
		number = 42;
		ready = true;
	}
}

在这里插入图片描述

只要数据需要被跨线程共享,就进行恰当的同步。

3.1.1 过期数据

NoVisibility演示了一种没有恰当同步的程序,它能够引起意外结果:过期数据。
当线程检查read变量时,它可能看到一个过期的值。
非线程安全的可变整数访问器:
在这里插入图片描述
线程安全的可变整数访问器:
在这里插入图片描述

3.1.2 非原子的64位操作

在这里插入图片描述
最低限的安全性应用于所有的变量,除了一个例外:没有声明为volatile 的64位数值变量(double和long)。Java允许64位的读或写划分为两个32位才值。如果读写发生在不同的线程,非volatile 的64位数可能会出现一个值的高32位和另一个值的低32位。

3.1.3 锁和可见性

在这里插入图片描述
当访问一个共享变量时,为什么要求所有线程由同一个锁同步。

为了保证一个线程崔数值进行的写入,其他线程也可见。

3.1.3 Volatile

弱同步形式:Volatile变量
Volatile确保对一个变量的更新以可预见的方式告知其他线程。
在这里插入图片描述

volatile的典型应用:状态检查标记。

加锁可以保证可见性与原子性;volatile变量只能保证可见性。

3.2 发布与逸出

发布一个对象:使它能够在当前范围之外的代码所使用。一个对象在尚未准备好时就将它发布,被称作逸出
发布对象:
在这里插入图片描述
允许内部可变的逸出:
在这里插入图片描述
隐式地允许this引用逸出:
在这里插入图片描述
内部类的实例包含了对封装实例隐含的引用。

3.2.1 安全构建的实践

不要让this引用在构造器期间逸出。
在构造函数中创建线程会导致this在构造期间逸出。
如果想在构造函数中注册监听器或启动线程,可以使用一个私有的构造方法和一个公共的工厂方法。
在这里插入图片描述

3.3 线程封闭

访问共享的、可变的数据要求使用同步。一个避免同步的方式就是使用不共享数据。
线程封闭是实现线程安全的最简单方式之一。
另一个常见使用线程限制的是应用池化的JDBC Connection对象。线程总是从池中获得一个Connection对象,并且用它处理单一请求,最后把它归还。

3.3.1 Ad-hoc 线程限制

Ad-hoc 线程限制是指维护线程限制性的任务全部落在实现上这种情况。
未经过设计而得到的线程封闭行为。

3.3.2 栈限制

在栈限制中,只有通过本地变量才可以触及对象。
类似局部变量,只存在当前执行线程中。

3.3.3 ThreadLocal

ThreadLocal 提供了get和set方法,可以将数值关联在线程上。
ThreadLocal 通常用于防止在基于可变的单体或者全局变量的设计中。
在这里插入图片描述

3.4 不可变性

为了满足同步的需要,另一种方法是使用不可变对象。

不可变对象永远是线程安全的。

在这里插入图片描述
在这里插入图片描述

3.4.1 Final域

Final对创建不可变对象提供了支持。

Final域是不能被修改的(指向的对象是可变的,这个对象仍然可被修改)。

3.4.2 使用volatile发布不可变对象

使用可变的容器,就必须使用锁以确保原子性;使用不可变对象,一旦一个对象获取了它的引用,用于不用担心其他线程会修改它的状态。
在这里插入图片描述

3.5 安全发布

在这里插入图片描述
由于可见性的问题,容器还是会在其他线程中被设置为一个不一致状态。这种不正确的发布会导致其他线程可以观察到“局部创建对象”。

3.5.1 不正确发布:当好对象变坏时

因为没有同步Holer对其他线程可见,所以称Holer是“非正确发布的”。
没有同步Holer会导致两种错误:其他线程会看到Holer域的过期值;

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

Java存储模型为共享不可变对象提供了特殊的初始化安全性保证。

3.5.3 安全发布模式

在这里插入图片描述
静态初始化器创建对象:

public static Holder holder = newHolder(42);

线程安全库中的容器提供了如下的线程安全保证:
在这里插入图片描述

3.5.4 高效不可变对象
3.5.5 可变对象

在这里插入图片描述

3.5.6 安全地共享对象

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/u011067966/article/details/84069241