Java多线程目录
1 背景
理解Java多线程的内存抽象逻辑请阅读java多线程内存模型,当代操作系统,处理器为提高处理速度,处理器与内存不直接进行交互,而是先将内存中的数据读取到内部缓存。Java多线程安全就是这样产生的。
2 volatile是什么
volatile是Java的一个关键字,在多线程并发中,volatile和synchronized都是重要的关键字,volatile比synchronized更轻量,因为volatile不需要上下文切换,它在多处理器中保证了共享变量的内存可见性。
3 内存可见性
变量存储在内存上,在Java中被volatile修饰的变量,Java线程内存模型会确保所有线程看到的这个变量值是一致的,也就是这个变量所在的内存在Java多个线程中看到的是一样的。
3.1 volatile怎样保证内存可见性。
被volatile修饰的变量会确保两件事
- 禁止指令重排
指令重排是个大概念,这里不详细介绍,指令重排就是我们代码的编译优化等,这个是JVM编译时做的,会影响我们的执行顺序,但不会影响执行结果。volatile禁止了指令重排,会让我们的程序更按照我们的想法执行。 - 内存可见性
volatile保证了在我们对volatile修饰的变量进行修改时,线程缓存会立刻刷入主内存。同时为保持多线程看到的变量值一致性,其他线程读取volatil变量会先从主内存读取值,再与线程缓存比较后使用,形成一个线程修改volatile变量,其他线程看到的结果是一致的。
4 volatile的局限性
注意在我们的普通编程中我们并不推荐使用volatile关键字,因为它有各种各样的线程,使用不当达不到多线程并发想要的效果。
4.1 操作并不是原子性
volatile关键字只保证了内存可见性与一致性,但它并不是原子操作,所以我们不能在多个线程内对这个变量都进行写操作。
4.2 运算结果并不依赖当前volatile变量的值,或者只有一个线程在修改volatileb变量。
也就是说我们在普通的多线程并发操作中并不能使用volatile关键字,只有满足上面条件时才使用,来保证volatile变量一修改,其他线程会立马可见。但无法保证多个线程同时对volatile变量写而造成的线程安全问题。
public class ThreadOne implements Runnable {
volatile int i = 0;
public void run() {
i++;
System.out.println(false + " " + Thread.currentThread().getName() + " " + i);
}
public static void main(String[] args) {
ThreadOne one = new ThreadOne();
for (int i = 1; i < 200; i++) {
new Thread(one,"" + i).start();
}
}
}
上述代码多个线程对volatile变量i进行了写操作,所以结果一般会出现线程安全问题。
4.3 变量不需要与其他的状态变量共同参与不变约束。
5 总结
- Java中每个线程都有单独的内存,所以多线程共享变量会有线程安全问题。
- volatile修饰的变量每次改变都会立刻刷入主内存。
- 每次使用volatile变量都会先从主内存读取在与线程内存的副本进行比较,后使用。
- volatile修饰的变量只能保证内存可见性,它不是原子操作。
- volatile会禁止指令重排,代码的顺序就是执行的顺序。
网友评论