线程安全问题
线程不安全:多个线程共享一个全局变量,当一个线程对其进行操作可能会受到其他线程的干扰,这是就是线程不安全;
线程安全:多个线程共享一个全局变量,不管通过怎样的调用方式,其结果都是预想的结果,这就是线程安全。
线程安全问题出现条件:
1、有多个线程运行
2、多个线程有同一共享资源
3、对共享的资源进行非原子性操作
比如进行10000次循环,线程安全的应该输出10000,但线程不安全的输出不为10000,没有得到期望结果
import java.util.concurrent.TimeUnit;
public class c8{
private static int value1 = 0;
private static int value2 = 0;
public static synchronized void increaseValue2(){
value2++;
}
public static void main(String[] args) {
for (int i = 0; i < 10000; i++) {
new Thread() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
value1++;
increaseValue2();
}
}.start();
}
while (Thread.activeCount() > 2) {//用来查看当前活跃线程
Thread.yield();
} //idea里面有一个监控线程,主线程
System.out.println("线程不安全:"+value1);
System.out.println("线程安全:"+value2);
}
}
运行结果:
线程不安全:9955
线程安全:10000
volatile
volatile特点:
1、可以保证线程之间的可见性,但不具备原子性
i++分为三个步骤,不具备原子性,不能被volatile修饰,因此volatile变量不能作为计数器;
/**
* i++分为3步骤:
* 1)从主内存获取i的值,缓存到当前线程的工作内存
* 2)在线程工作内存对i进行+1
* 3)将i的最新值写入到主内存中
*/
2、volatile开销非常低,且不会对线程造成阻塞,但同步性较差
volatile解决的问题:
1、防止指令重排序;
重排序是指编译器和处理器为了优化程序性能而对指令进行排序的手段,重排序不会对存在数据依赖的操作进行重排序,重排序不会影响程序执行结果。
重排序在单线程条件下可以保证结果正确性,但在多线程环境下可能会影响结果。
2、解决缓存一致性问题,即可见性
volatile变量在一个线程内改变时,会强制刷新到主内存中,保证共享变量值的一致性
volatile原理:
在JVM底层中,volatile是采用内存屏障实现,在源码中,volatile修饰的变量存在一个“ lock. ”前缀,相当于一个内存屏障
具体保障:
(1)确保指令重排序时不会将内存屏障后面的排到内存屏障之前
(2)确保指令重排序时不会将内存屏障前面的排到内存屏障之后
(3)确保在执行到内存屏障修饰的指令时,前面的代码全部执行完成
(4)强制将线程工作内存中的的值刷新到主内存中
(5)写操作时,会导致其他线程(使用共享变量)工作内存中的缓存数据会失效,下一次就需要去主内存中再次读取。
volatile使用场景:
1、修饰boolean类型的值
2、状态标记利用顺序性
private boolean flag = false;
private Test test;
public Test load(){
if(!falg){
test = new Test();//a
flag = true;//b
}
return test;
}
// 线程A:b和a语句发生重排序,b赋值flag为true之后发生了线程的上下文切换
// 线程B:flag==true,会返回一个未初始化的test
3、单例模式
如果不使用volatile,可能会导致 无序写入 (某个线程可能会获得一个未完全初始化的实例)
在多线程条件下,getInstance()可能会创建两个Singleton对象,因此需要双重检验加锁。
private static volatile Singleton instance;
private static Lock lock = new ReentrantLock();
private static Singleton getInstance(){
if(instance == null){
try {
lock.lock();
if(instance == null){
instance = new Singleton();
}
}finally {
lock.unlock();
}
}
return instance;
}
}