1、可见性
volatile概念:volatile关键字的主要作用是使用变量在多个线程间可见
例1:
public class Volatile01 extends Thread {
private volatile boolean isRunning = true;//volatile
private void setRunning(boolean isRunning) {
this.isRunning = isRunning;
}
@Override
public void run() {
System.out.println("进入run方法");
while(isRunning) {
//...
}
System.out.println("线程结束");
}
public static void main(String[] args) throws InterruptedException {
Volatile01 rt = new Volatile01();
rt.start();
Thread.sleep(3000);//防止先执行rt.setRunning(false),然后才进入run方法
rt.setRunning(false);
System.out.println("main结束,且isRunning=" + rt.isRunning);
}
}
(1)当isRunning未加volatile关键字的时候,执行结果:run方法一直未结束
(2)当isRunning加上volatile关键字后,执行结果:
实例总结:
在java中,每一个线程都会有一块工作内存区,其中存放着所有的线程共享的主内存中的变量值的拷贝。当线程执行时,他在自己的工作内存中操作这些变量。为了存放一个共享的变量,一个线程通常先获取锁定并去清除它的内存工作区,把这些共享变量从所有线程的共享内存区中正确的装入到他自己所在的工作内存区中,当线程解锁时保证该工作内存区中变量的值写回到共享内存中。
一个线程可以执行的操作有使用(use)、赋值(assign)、装载(load)、存储(store)、锁定(lock)、解锁(unlock)。
而主内存可以执行的操作有读、写、锁定、解锁,每个操作都是原子的。
volatile的作用就是强制线程到主内存(共享内存)里去读取变量,而不去线程工作内存区里去读取,从而实现类多个线程间的变量可见,也就是满足线程安全的可见性。
2、非原子性
volatile关键字不具备原子性
例子2:
import java.util.concurrent.atomic.AtomicInteger;
public class Volatile02 extends Thread {
//private volatile static int count = 0;
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount() {
for(int i = 0; i < 1000; i++) {
//count++;
count.incrementAndGet();//不带参数,相当于++,可带参数,参数相当于一次加多少
}
System.out.println(count);
}
@Override
public void run() {
addCount();
}
public static void main(String[] args) {
Volatile02[] array = new Volatile02[10];
for(int i = 0; i < 10; i++) {
array[i] = new Volatile02();
}
for (Volatile02 volatile02 : array) {
volatile02.start();
}
}
}
当使用volatile的时候,代码执行结果:
当使用AtomicInteger的时候:
例子3:
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicUse {
private static AtomicInteger count = new AtomicInteger(0);
//多个addAndGet在一个方法内是非原子性的,需要加synchronized进行修饰,保证4个addAndGet整体原子性
public synchronized int multiAdd() {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
count.addAndGet(1);
count.addAndGet(2);
count.addAndGet(3);
count.addAndGet(4);
return count.get();
}
public static void main(String[] args) {
final AtomicUse au = new AtomicUse();
List<Thread> list = new ArrayList<Thread>();
for(int i = 0; i < 100; i++) {
list.add(new Thread(new Runnable() {
@Override
public void run() {
System.out.println(au.multiAdd());
}
}));
}
for (Thread thread : list) {
thread.start();
}
}
}
volatile关键字虽然拥有多个线程之间的可见性,但却不具备同步性(也就是原子性),可以算上是一个轻量级的synchronized,性能比synchronized强很多,不会造成阻塞(很多开源框架里,比如netty的底层代码就大量使用了volatile,可见netty性能一定非常不错的。)这里需要注意:一般volatile用于只针对多个线程的可见的变量操作,并不能代替synchronized的同步功能。
示例总结:
volatile关键字只具有可见性,没有原子性。要实现原子性建议使用atomic类的系列对象,支持原子性操作(注意atomic类只保证本身方法原子性,并不保证多次操作的原子性)