volatile关键字:
- 能够保证volatile变量的可见性
- 不能保证volatile变量复合操作的原子性
volatile如何实现内存可见性:
深入来说:通过加入内存屏障和禁止重排序优化来实现的。
- 对volatile变量执行写操作时,会在写操作后加入一条store屏障指令
- 对volatile变量执行读操作时,会在读操作前加入一条load屏障指令
通俗地讲:volatile变量在每次被线程访问时,都强迫从主内存中重读该变量的值,而当该变量发生变化时,又会强迫线程将最新的值刷新到主内存。这样任何时刻,不同的线程总能看到该变量的最新值。
线程写volatile变量的过程:
- 改变线程工作内存中volatile变量副本的值
- 将改变后的副本的值从工作内存刷新到主内存
线程读volatile变量的过程:
- 从主内存中读取volatile变量的最新值到线程的工作内存中
- 从工作内存中读取volatile变量的副本
volatile不能保证volatile变量复合操作的原子性:
private int number = 0;
number++; //不是原子操作
它分为三步:
读取number的值
将number的值加1
写入最新的number的值
保证number自增操作的原子性:
- 使用synchronized关键字
- 使用ReentrantLock
- 使用AtomicInteger
使用synchronized关键字
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @author InJavaWeTrust
*/
public class TestSyn implements Runnable {
private int number = 0;
public int getNumber() {
return this.number;
}
public void run() {
increase();
}
public void increase() {
synchronized (this) {
this.number++;
}
}
public static void main(String[] args) {
ExecutorService exec = Executors.newFixedThreadPool(1000);
TestSyn syn = new TestSyn();
for (int i = 0; i < 1000; i++) {
exec.submit(syn);
}
System.out.println("number : " + syn.getNumber());
exec.shutdown();
}
}
使用ReentrantLock
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author InJavaWeTrust
*/
public class TestRee implements Runnable {
private Lock lock = new ReentrantLock();
private int number = 0;
public int getNumber() {
return this.number;
}
public void run() {
increase();
}
public void increase() {
lock.lock();
try {
this.number++;
} finally {
lock.unlock();
}
}
public static void main(String[] args) {
TestRee ree = new TestRee();
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
exec.submit(ree);
}
System.out.println("number : " + ree.getNumber());
exec.shutdown();
}
}
使用AtomicInteger
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @author InJavaWeTrust
*/
public class TestAtomic implements Runnable {
private static AtomicInteger number = new AtomicInteger(0);
public void run() {
increase();
}
public void increase() {
number.getAndAdd(1);
}
public static void main(String[] args) {
TestAtomic ato = new TestAtomic();
ExecutorService exec = Executors.newFixedThreadPool(1000);
for (int i = 0; i < 1000; i++) {
exec.submit(ato);
}
System.out.println("number : " + number.get());
exec.shutdown();
}
}