eg.1 重排序现象
public class NoVisibility {
private static boolean ready;
private static int number;
private static class ReaderThread extends Thread {
public void run() {
while (!ready)
Thread.yield();
System.out.println(number);
}
}
public static void main(String[] args) {
new ReaderThread().start();
number = 42;
ready = true;
}
}
eg.2 数据失效
@NotThreadSafe
public class MutableInteger {
private int value;
public int get() {
return value;
}
public void set(int value) {
this.value = value;
}
}
eg.2.1 蜜汁加锁
public class SynchronizedInteger {
@GuardedBy("this") private int value;
public synchronized int get() {
return value;
}
public synchronized void set(int value) {
this.value = value;
}
}
基于volatile变量的特性,通常只需要对写入操作进行适当的加锁来控制并发
发布: 表示对象能够在当前作用域之外的代码中使用
class UnsafeStates {
private String[] states = new String[]{
"AK", "AL" /*...*/
};
public String[] getStates() {
return states;
}
}
任何调用者都能修改数组的内容,states对象已经逸出了他所在的作用域
public class ThisEscape {
public ThisEscape(EventSource source) {
source.registerListener(new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
void doSomething(Event e) {
}
interface EventSource {
void registerListener(EventListener e);
}
interface EventListener {
void onEvent(Event e);
}
interface Event {
}
}
当我们new ThisEscape对象的时候,该线程会持有doSomething的this引用,此时ThisEscape还没有实例完成(没有返回引用),导致this引用逸出
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;
}
void doSomething(Event e) {
}
interface EventSource {
void registerListener(EventListener e);
}
interface EventListener {
void onEvent(Event e);
}
interface Event {
}
}
当访问共享的可变数据时,通常需要使用同步.一种避免使用同步方式就是不共享数据.如果再单线程内访问数据,就不需要同步,这种技术被称之为线程封闭
指维护线程封闭性的职责完全有程序实现来承担
只能通过局部变量才能访问对象
eg.7
public class Animals {
Ark ark;
Species species;
Gender gender;
public int loadTheArk(Collection candidates) {
SortedSet animals;
int numPairs = 0;
Animal candidate = null;
// animals confined to method, don't let them escape!
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;
}
class Animal {
Species species;
Gender gender;
public boolean isPotentialMate(Animal other) {
return species == other.species && gender != other.gender;
}
}
enum Species {
AARDVARK, BENGAL_TIGER, CARIBOU, DINGO, ELEPHANT, FROG, GNU, HYENA,
IGUANA, JAGUAR, KIWI, LEOPARD, MASTADON, NEWT, OCTOPUS,
PIRANHA, QUETZAL, RHINOCEROS, SALAMANDER, THREE_TOED_SLOTH,
UNICORN, VIPER, WEREWOLF, XANTHUS_HUMMINBIRD, YAK, ZEBRA
}
enum Gender {
MALE, FEMALE
}
class AnimalPair {
private final Animal one, two;
public AnimalPair(Animal one, Animal two) {
this.one = one;
this.two = two;
}
}
class SpeciesGenderComparator implements Comparator {
public int compare(Animal one, Animal two) {
int speciesCompare = one.species.compareTo(two.species);
return (speciesCompare != 0)
? speciesCompare
: one.gender.compareTo(two.gender);
}
}
class Ark {
private final Set loadedAnimals = new HashSet();
public void load(AnimalPair pair) {
loadedAnimals.add(pair);
}
}
}
new TreeSet引用保存在animals中,此时只有一个引用指向animals,该引用封闭在局部变量中,因此被封闭在执行线程中。如果发布了对animals的引用,就会造成逃逸。
提供get和set方法,每个使用该变量的线程都存一份独立的副本,get总是返回当前执行线程在调用set时设置的最新值
ThreadLocal对象通常用于防止对可变的单实例变量或全局变量进行共享。
假设你需要将一个单线程应用程序移植到多线程环境中,通过将共享的全局变量转换为ThreadLocal对象(如果全局变量的语义允许),可以维持线程安全性。
ThreadLocal变量类似于全局变量,它能降低代码的可重用性,并在类之间引入隐含的耦合性,因此在使用时要格外小心。
安全地发布一个对象,对象的应用以及对象的状态必须同时对其他线程可见。一个正确构造的对象可以通过以下方式来安全地发布:
在线程安全容器内部的同步意味着,在将对象放入到某个容器,例如Vector或synchronizedList时,将满足上述最后一条需求。如果线程A将对象X放入一个线程安全的容器,随后线程B读取这个对象,那么可以确保B看到A设置的X状态,即便在这段读/写X的应用程序代码中没有包含显式的同步。尽管Javadoc在这个主题上没有给出很清晰的说明,但线程安全库中的容器类提供了以下的安全发布保证:
类库中的其他数据传递机制(例如Future和Exchanger)同样能实现安全发布。
通常,要发布一个静态构造的对象,最简单和最安全的方式是使用静态的初始化器:
public static Holder holder = new Holder(42);
静态初始化器由JVM在类的初始化阶段执行。由于在JVM内部存在着同步机制,因此通过这种方式初始化的任何对象都可以被安全地发布[JLS 12.4.2]。
对象的发布需求取决于它的可变性:
在并发程序中使用和共享对象时,遵循如下实用策略