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. 不变性
如果某个对象在被创建后其状态就不能被修改,那么这个对象就称为不可变对象。不可变对象的固有属性之一便是线程安全性,不可变对象只有一种状态,且有构造函数控制。
当满足以下条件,对象才是不可变的:
- 对象创建以后其状态就不能修改。
- 对象的所有域都是 final 类型。
- 对象是正确创建的(在对象创建期间,this 引用没有逸出)