本文参考《java并发编程实战》,java并发必读书籍
关键词
- 线程与锁
- 共享的(Shared)状态:多个线程同时访问
- 可变的(Mutable)状态:变量的值在其生命周期内可以发生变化
- 同步机制:Synchronized, volatile类型变量, 显示锁(Explicit Lock),原子变量
- 解决可变的状态变量并发错误的3个方案1.不共享,2.改为不可变,3.使用同步
- 线程安全类与线程安全程序
一、什么是线程安全性
1.线程安全性定义
当多个线程访问某个类时,不管运行时环境采用何种调度方式,或者线程将如何交替执行,主调代码中不需要任何额外的同步和协同,这个类就能表现出正确的行为,那么就称这个类时线程安全的。
2.备注说明
- a.最核心的概念就是 正确性
- b.在线程安全类中封装了必要的同步策略,客户端(主调代码)无需进一步采取同步策略
二、原子性
1.竞态条件
最为常见的竞态条件是:先检查后执行(Check-Then-Act)
例如单例中的,这个单例是有问题的,可以参考特别完整及简单的单例模式
pulic class Singleton {
private Singleton instance;
private Singleton() {
}
public static Singleton() {
if(instance == null) {//先检查,后执行
instance = new Singleton();
}
return instance
}
}
2、复合操作与原子操作
复合操作:是一组需要以原子方式执行的操作
原子操作:对于访问同一个状态的所有操作来说,这个操作是一个以原子方式执行的操作
三、加锁机制
1.内置锁
java提供的内置锁为同步代码块(Synchronize Block)
包含俩部分:一个作为锁的对象引用;一个作为由这个锁保护的代码块。
静态的synchronized方法以Class对象作为锁
内置锁也称为互斥体、互斥锁
2.重入
重入意味着获取锁的操作的粒度是线程,而不是调用
重入的实现方式:为每个锁关联一个获取计数值和一个所有者线程
重入进一步提升了加锁行为的封装性,因而简化了面向对象并发代码的开发
重入例子
public class Widget {
public synchronized void doSomething() {
...
}
}
public class LoggingWidget extends Widget {
public synchronized void something() {
...
super.doSomething();
}
}
四、用锁来保护状态
共享状态的变量以独占的方式来访问,也就是以 串行形式来访问。
对于可能被多个线程同时访问的可变状态变量,在访问它的时候都需要持有同一个锁,在这种情况下,我们称状态变量是由这个锁保护的
对于每个包含的多个变量的不变性条件,其中涉及的所有变量都需要由同一个锁来保护
五、活跃性与性能
在简单性与性能之间存在互相制约因素。
当实现某个点同步策略时,一定不要盲目地为了性能而牺牲简单性(这可能破坏安全性)
当执行时间较长的计算或者可能无法快速完成的操作时(例如,网络I/O或者控制台I/O),一定不要持有锁