要让一个类是线程安全的,可以将这个任务委托给类中的状态变量,让这些状态变量成为线程安全的,那么这个类也就是线程安全的了。
比如一个响应事件的类,鼠标事件和键盘事件是完全无关的,也就是说这连个变量是彼此独立的,那么可以将类的线程安全委托给这两个变量。
public class VisualComponent {
private final List<KeyListener> KeyListeners = new CopyOnWriteArrayList<KeyListener>();
private final List<MouseListener> MouseListeners = new CopyOnWriteArrayList<MouseListener>();
public void addKeyListener(KeyListener keyListener){
KeyListeners.add(keyListener);
}
public void addMouseListener(MouseListener mouseListener){
MouseListeners.add(mouseListener);
}
}
通过CopyOnWriteArrayList这个线程安全的集合类将两个状态变量设置为线程安全的。保证了整个类是线程安全的。
再比如连个状态变量有联系的。定义一个数值区间的类,也就是值域,当改变值域时,设置值域上限时要大于当时的最小值,设置值域下限时要小于当时的最大值。这样两个变量就有了联系,当我们把类的线程安全委托给这两个变量时,就会出现问题。
public class NumberRange {
private final AtomicInteger lower = new AtomicInteger(0);
private final AtomicInteger upper = new AtomicInteger(10);
public void setLower(int i ){
if (i > upper.get()){
throw new IllegalArgumentException(i+"不能比大数大!");
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
lower.set(i);
System.out.println("设置成功!"+lower.get());
}
public void setUpper(int i){
if (i < lower.get()){
throw new IllegalArgumentException(i+"不能比小数小!");
}
upper.set(i);
System.out.println("设置成功!"+upper.get());
}
public void getRange(){
System.out.println("区间"+lower+"--"+upper);
}
public static void main(String[] args) {
NumberRange numberRange = new NumberRange();
new Thread(new Runnable() {
@Override
public void run() {
numberRange.setLower(5);
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
numberRange.setUpper(4);
}
}).start();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
numberRange.getRange();
}
}
可以看到出现了问题,两个线程,值域初始值是0-10,第一个线程想把值域下限设置为5,满足判断小于上限10,但是判断完了让他休眠1秒,此时CPU被第二个线程抢去,第二个线程想把上限设置为4,满足大于下限0,将上限设置为4,第二个线程执行完之后第一个线程继续执行,将下限设置为了5。
总结:如果一个类由多个彼此独立的线程安全的状态变量组成,并且类的操作不包含无效状态转换,那么可以将线程安全委托给这些状态变量。
如果一个状态变量是线程安全的,没有任何不变约束限制他的值,并且没有任何状态转换限制他的操作,那么他可以被安全发布。