Java Concurrency —— 线程封闭

1. 线程封闭

当访问共享可变数据时,通常需要使用同步。如果仅在单线程内访问数据,就不需要同步,这种技术称为线程封闭。

2. Ad-hoc 线程封闭

Ad Hoc 源自于拉丁语,意思是“for this”引申为“for this purpose only”,即“为某种目的设置的,特别的”意思。Ad-hoc 线程封闭,指维护线程封闭完全由程序来承担,这是难以实现且脆弱的。

3. 栈封闭

在栈封闭中,只能通过局部变量才能访问对象。局部变量的固有属性之一就是封闭在执行线程中。它们位于执行线程的栈中,其他线程根本无法访问。栈封闭比 Ad-hoc 线程封闭更易于维护,也更加健壮。

对于基本类型的局部变量,无论如何都不会破坏栈封闭性,因为任何方法都无法获得对基本类型的引用:

public void test01() {
	int i = 0;
	i ++ ;
}

在维持对象引用的栈封闭性时,程序员需要多做一些工作以确保被引用的对象不会“逸出”,也就是不要轻易把对象给别人使用:

public static void test01() {
	StringBuilder stringBuilder = new StringBuilder();
	stringBuilder.append("Hello").append("World");
	System.out.println(stringBuilder.toString());
}

如果在线程内部上下文中使用非线程安全的对象,那么该对象仍然时线程安全的。

就如上面例子,StringBuilder 时 JDK 1.5 加入的类,它与 StringBuffer 几乎无异,只不过它是线程不安全的,方法全都没有 synchronized 修饰,但往往很多时候,我们只是在方法中使用字符串拼接来避免产生大量字符串垃圾,使用 StringBuffer 反而小题大做,甚至 synchronized 加锁与解锁也可能有时间开销。

4. ThreadLocal 类

更规范的维持线程封闭的一种方法时使用 ThreadLocal,ThreadLocal 已经不是数据共享了,而是为每个线程保持一个独立的副本,比如:多线程 JDBC 的 Connection 保存在 ThreadLocal 中:

private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
	@Override
	protected Connection initialValue() {
		return DriverManager.getConnection(DB_URL);
	}
};

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

在 Spring Web 中也能找到使用的地方:

public abstract class RequestContextHolder {

	private static final ThreadLocal<RequestAttributes> requestAttributesHolder =
			new NamedThreadLocal<>("Request attributes");

	private static final ThreadLocal<RequestAttributes> inheritableRequestAttributesHolder =
			new NamedInheritableThreadLocal<>("Request context");
    ...
}

5. 不变性

如果某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象。不可变对象的固有属性之一便是线程安全性,不可变对象只有一种状态,且有构造函数控制。

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

扫描二维码关注公众号,回复: 10384059 查看本文章
  • 对象创建以后其状态就不能修改。
  • 对象的所有域都是 final 类型。
  • 对象是正确创建的(在对象创建期间,this 引用没有逸出)
发布了48 篇原创文章 · 获赞 2 · 访问量 6332

猜你喜欢

转载自blog.csdn.net/qq_39291919/article/details/103405041