文章目录
对象的共享
3.1可见性
/**
* 主线程和读线程都将访问共享变量ready和number
*/
public class NoVisibility {
private static boolean ready;
private static int number;
/**
* 读线程直到ready的值变为true,然后输出number的值。
*/
private static class ReaderThread extends Thread {
@Override
public void run() {
while (!ready) {
Thread.yield();
}
// 看起来会输出42,但事实上可能输出0,或者上面的循环根本无法终止
System.out.println(number);
}
}
/**
* 主线程启动读线程,然后设置number和ready的值
*/
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
3.1.1失效数据
/**
* 因为get和set都是在没有同步的情况下访问value的,失效值问题更容易出现。
*/
@NotThreadSafe
public class MutableInteger {
private int value;
public int get() {
return value;
}
public void set(int value) {
this.value = value;
}
}
@ThreadSafe
public class SynchronizedInteger {
@GuardedBy("this")
private int value;
// 仅对set方法进行同步是不够的,调用get的线程仍然会看见失效值
public synchronized int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
}
3.1.2非原子的64位操作
3.1.3加锁与可见性
3.1.4volatile
变量
/**
* 通过类似于数绵羊的方法进入休眠状态,为了使示例能正确执行,asleep必须为volatile变量。否则,
* 当asleep被另一个线程修改时,执行判断的线程却发现不了。也可以用锁来确保asleep更新操作的可见性,
* 但这将使代码变得更加复杂。
*/
volatile boolean asleep;
// 其他操作
// 检查某个状态标记以判断是否退出循环
while (!asleep) {
countSomeSheep();
}
3.2发布与逸出
public static Set<Secret> knownSecrets;
public void initialize() {
knownSecrets = new HashSet<>();
}
/**
* 发布了本应为私有的状态数组,可能会出现问题,因为任何调用者都能修改这个数组的内容。
*/
class UnsafeStates {
private String[] states = new String[] {
"AK", "AL", ...
};
public String[] getStates() {
return states;
}
}
/**
* 当ThisEscape发布EventListener时,也隐含地发布了ThisEscape实例本身,因为在这个内部类的实例中包含了
* 对ThisEscape实例的隐含引用。
*/
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
// ThisEscape类实例的其他初始化操作
}
}
安全的对象构造过程
public class SafeListener {
private final EventListener listener;
private SafeListener() {
listener = new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
};
}
public static SafeListener newInstance(EventSource source) {
SafeListener safe = new SafeListener();
source.registerListener(safe.listener);
return safe;
}
}
3.3线程封闭
3.3.1Ad-hoc线程封闭
3.3.2栈封闭
public int loadTheArk(Collection<Animal> candidates) {
SortedSet<Animal> animals;
int numPairs = 0;
Animal candidate = null;
// animals被封闭在方法中,不要使它们逸出
animals = new TreeSet<>(new SpeciesGenderComparator());
animals.addAll(candidates);
for (Animal a : animals) {
if (candidate == null || !candidate.isPotentialMate(a)) {
candidate = a;
} else {
ark.load(new AnimalPair(candidate, a));
++numPairs;
candidate = null;
}
}
return numPairs;
}
3.3.3ThreadLocal
类
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal() {
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectHolder.get();
}
3.4不变性
/**
* 尽管Set是可变的,但其构造完成之后就无法对其进行修改。
*/
@Immutable
public final class ThreeStooges {
private final Set<String> stooges = new HashSet<>();
public ThreeStooges() {
stooges.add("Moe");
stooges.add("Larry");
stooges.add("Curly");
}
public boolean isStooge(String name) {
return stooges.contains(name);
}
}
3.4.1final
域
3.4.2示例:使用volatile
类型来发布不可变对象
@Immutable
class OneValueCache {
private final BigInteger lastNumber;
private final BigInteger[] lastFactors;
public OneValueCache(BigInteger i, BigInteger[] factors) {
lastNumber = i;
lastFactors = Arrays.copyOf(factors, factors.length);
}
public BigInteger[] getFactors(BigInteger i) {
if (lastNumber == null || !lastNumber.equals(i)) {
return null;
} else {
return Arrays.copyOf(lastFactors, lastFactors.length);
}
}
}
/**
* 与cache相关的操作不会相互干扰,因为OneValueCache是不可变的,并且在每条相应的代码路径中只会访问它一次。
* 通过使用包含多个状态变量的容器对象来维持不变性条件,并使用一个volatile类型的引用来确保可见性,使得
* VolatileCachedFactorizer在没有显式地使用锁的情况下仍然是线程安全的。
*/
@ThreadSafe
public class VolatileCachedFactorizer extends HttpServlet {
private volatile OneValueCache cache = new OneValueCache(null, null);
public void service(ServletRequest req, ServletResponse resp) {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = cache.getFactors(i);
if (factors == null) {
factors = factor(i);
cache = new OneValueCache(i, factors);
}
encodeIntoResponse(resp, factors);
}
}
3.5安全发布
// 不安全的发布
public Holder holder;
public void initialize() {
holder = new Holder(42);
}
3.5.1不正确的发布:正确的对象被破坏
public class Holder {
private int n;
public Holder(int n) {
this.n = n;
}
public void assertSanity() {
if (n != n) {
throw new AssertionError("This statement is false.");
}
}
}
3.5.2不可变对象与初始化安全性
3.5.3安全发布的常用模式
public static Holder holder = new Holder(42);