1.线程安全定义
当多个线程访问某个类,不管运行环境采用何种调度方式或者这些线程将如何交替执行,并且在主调代码中不需要任何额外的同步或者协同,这个类都能表现出正确的行为,那么就称这个类是线程安全的。
无状态的对象本身就是线程安全的
2.原子性
当一个操作是原子操作,那么这个操作就可以认为是线程安全的。而如果这个操作是一个复合操作(非原子操作),那么在并发的情况下,就会出现不正确的结果,我们称之为:竞态条件
3.竞态条件
1.延迟初始化操作
延迟初始化的常用方式一般是单例模式(没有加同步语句)
2.复合操作
2.1 检查-执行
public class Singleton{
private static Sinleton sInstance;
public static Sinleton getInstance() {
if(sInstance == null) {
sInstance = new Sinleton();
}
return sInstance;
}
}
2.2 读取-修改-写入(例如:count++)
4.加锁机制
多个原子操作合并在一起,就会变成复合操作,而复合操作具有线程安全的问题,我们通过加锁来实现复合操作原子化。
要保持状态的一致性,就需要在单个原子操作中更新所有相关的状态变量
4.1 内置锁
为了避免显示的声明锁,Java提供了内置的锁机制。内置锁支持原子性:同步代码块(synchronized)
4. 2重入
内置锁是支持可重入的。这样用来防止以下的死锁
public class Widget {
public synchronized void doSomething() {
}
}
public class LoggingWidget extends Widget {
public synchronized void doSometing() {
super.doSomething();
}
}
5.用锁来保护状态
1.锁保护代码路径,串行访问即对共享变量的独占访问,以确保状态的一致性
2.获取与对象关联的锁,并不能阻止其他线程访问该对象,只能阻止其他线程获得同一个锁
3.每一个共享变量和可变变量都应该只由一个锁来保护