并发是多个线程同时去做不同的事情,但是在多线程同时去做同一件事情的时候就可能会产生并发问题
多线线程为什么会有高并发问题
因为在Java中每个变量都存储在主内存中,而每个线程也都有自己的工作内存,当线程对该变量进行修改操作时都是从主内存copy变量到自己的工作内存进行,完成后在同步到主内存,在这个同步过程中此时可能就会有其他线程也请求了这个变量数据,就会导致该线程实际上请求的是错误数据
并发的三要数
- 原子性
在一个操作中,CPU不可以在中途进行暂停然后在进行操作,要么完成要么不执行
- 可见性
多个线程在访问一个变量时,有一条线程对该变量进行的修改,则其他所有的线程都应立即看到该修改
- 有序性
如何解决并发问题
- Volatile
- 特性
- 特性
使用volatile修饰的变量只能保证该变量的可见性,不能保证的原子性,因为被volatile修饰后会将本地内存的变量强制刷新到主内存,在对内存写的时候,会使其他内存的缓存无效,其他线程读会从主内存读,volatile的写操作对其他线程可见
1. 列如在下面的代码中,当一线程执行write方法,线程2执行multiply方法,最后结果不一定是4,首先1和2步骤是没有关联的,3和4也没有关联,所以 编译器对1和2、3和4是可以进行重排序的相关操作,于是在执行过程中,线程1会先将flag写入为true,而此时线程B判断为真,这时候由于线程1还未将a=2写入,此时线程B获取的a的值就为0,所以在多线程中我们应该对可以为flag加长volatile关键字,禁止他进行重排序,保证程序运行的有序性
int a = 0;
bool flag = false;
public void write() {
a = 2; //1
flag = true; //2
}
public void multiply() {
if (flag) { //3
int ret = a * a;//4
}
}
2. volatile并不能保证原子性,比如对于i++这种复合操作就不起作用了,因为当多个线程线程同时去操作这同一个变量的时候,会产生堵塞,此时线程1线程2获取到的值都是1,然后线程1进行自增,而线程2需要等线程1执行完成才能执行,当线程1执行完成后,主内存里i已经=2了,但是此时线程2去执行自增时是用的的i=1,所以当线程2执行完成后,i还是等2,虽然加了volatile能够强制更新缓存,但是也只是在线程进行读取的时候才会重新更新缓存,而此时线程2早已经读取过了,所以未更新缓存为主内存的值