1.原子性与线程安全
多个线程操作一个全局变量int i,操作是i++。
1.可见性
通过volatile关键字是解决线程安全问题的必要条件。
2.原子性
保证原子性也是解决线程安全问题的必要条件。
因为i++不是原子操作,读取值,加一和赋值操作是分开的,所以有可能在读取值之后(读取到当前线程副本中),i值(主线程中的本尊)就发生了变化,也有可能i在被赋值之前发生不可预期的变化。
3.举例——为状态组件中使用状态
servlet是无状态的,但是如果需要用无状态的servlet去记录一个状态(比如请求次数),这个时候要么使用同步,要么使用原子变量。
2.状态
“编写线程安全的代码,本质上就是管理对状态的访问”,而且通常是共享的可变的状态。
1.无状态
无状态就不需要注意线程安全,或者不可变的状态,比如String或者一些Immutable的变量。
“无状态的对象永远是线程安全的”。
2.同步
对于有状态并且状态可变的情况,使用原子变量或者同步来解决。
3.线程安全的类
线程安全的类是在使用的时候,不需要使用者自己去考虑额外的同步。
比如SynchronizedMap,在内部方法的调用上,封装了一个互斥锁,这个动作是在内部封装好的,使用者不需要自己使用额外的同步操作就可以得到线程安全的保证,这个就是线程安全的类,代码如下:
private static class SynchronizedMap<K,V>
implements Map<K,V>, Serializable {
private static final long serialVersionUID = 1978198479659022715L;
private final Map<K,V> m; // Backing Map
final Object mutex; // Object on which to synchronize
SynchronizedMap(Map<K,V> m) {
if (m==null)
throw new NullPointerException();
this.m = m;
mutex = this;
}
SynchronizedMap(Map<K,V> m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
public int size() {
synchronized (mutex) {return m.size();}
}
……
}
4.竞争
检查在运行
check-then-act,检查在运行。
check条件通过的时候,在act的时候,该条件的状态可能已经改变。
举例1:
比如篮子里如果有小于5个苹果,我就向篮子中增加苹果,当我真正向篮子中增加苹果这个动作执行的时候,篮子中可能已经有超过5个苹果了,就会导致新添加的苹果洒出来了)。
举例2:
比如判断在系统的某个路径下,如果不存在一个文件,就去创建该文件。但是真正去创建的这个动作进行的时候,可能该文件已经存在了,这个时候就会造成已有文件被覆盖。
复合操作
给一系列的复合操作加锁,从结果上看相当于使用了原子操作,可能性能会有不同。
多个关联的状态变量
对这多个关联的状态变量的修改,要放在一个原子操作或者放到一个锁里面。