Java线程安全问题
Java线程安全性问题
- 原子性:一个或多个线程操作 CPU 执行的过程中被中断,互斥性称为操作的原子性
- 可见性:一个线程对共享变量的修改,其他线程不能立刻看到。要保证一个线程对主内存的修改可以及时的被其他线程观察到
- 有序性:程序执行的顺序没有按照代码的先后顺序执行
原子性安全
JDK 里面提供了很多 atomic 类,比如 AtomicInteger、AtomicLong、AtomicBoolean 等等,这些类本身可以通过 CAS 来保证操作的原子性。另外 Java 也提供了各种锁机制,来保证锁内的代码块在同一时刻只能有一个线程执行,比如使用 synchronized 加锁,保证一个线程在对资源进行读、写时,其他线程不可对此资源进行操作,从而保证了线程的安全性
可见性安全
同样可以通过 synchronized 关键字加锁来解决,与此同时,java 还提供了 volatile 关键字,要优于 synchronized 的性能,同样可以保证修改对其他线程的可见性。volatile 一般用于对变量的写操作不依赖于当前值的场景中,比如状态标记量等
有序性问题
主要来自指令重排序带来的有序性问题。可以通过 synchronized 关键字定义同步代码块或者同步方法保障有序性,另外也可以通过 Lock 接口来保障有序性
有序性-happens-before原则
- 程序次序规则:一个线程内,按照代码顺序,书写在前面的操作先行发生于写在后面的操作(虽然虚拟机可能进行了重排序,但是程序执行的顺序看起来是按照在代码书写的样式执行的。只会对数据不存在依赖性的指令进行重排序);这条规则只能保障单线程中保持正 确性,而无法在多线程中保持正确性
- 锁定规则:一个unlock操作先行发生于后面对同一个资源lock的操作(对一段代码或一个变量,只有锁定了资源的锁先执行了unlock操作,下一个线程才有可能执行lock操作。这里需要注意,当一个锁再一次进入同一个锁的时,这时候是否先执行unlock还是怎样??不会,这是可重入锁,不会进行释放,而是计数量会-1)
- volatile变量规则:对一个变量的写操作先行发生于后面这个变量的读操作
- 传递规则:如果操作A先发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C
- 线程启动规则:Thread对象的start()方法先行发生于此线程的每一个动作
- 线程终结规则: 线程中所有的操作都先行发生于线程的终止检测,我们可以通过Thread.join()方法结束、Thread.isAlive()的返回手段检测到线程已经终止执行
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 cloud_fly blog!