read、load、use、assign、store、write
public class HelloWorld {
private int data;
public void increment() {
data++;
}
}
HelloWorld helloWorld = new HelloWorld();
new Thread() {
public void run() {
helloWorld.increment();
}
}.start();
new Thread() {
public void run() {
helloWorld.increment();
}
}.start();
连环炮:Java内存模型 -> 原子性、可见性、有序性 -> volatile -> happens-before / 内存屏障
new Thead() {
public void run() {
data++;
}
}.start();
new Thread() {
public void run() {
while(data == 0) {
Thread.sleep(100);
}
}
}.start();
线程1在执行的时候,线程2不可对data进行操作,这个时候就具有原子性。如果两个线程都能对data进行更新,那就不具有原子性。
flag = false;
// 线程1
prepare(); // 准备资源
flag=true;
// 线程2
while(!flag) {
Thread.sleep(100);
}
execute(); // 基于准备好的资源执行操作
具有有序性,不会发生指令重拍导致我们的代码异常;不具备有序性,则会发生指令重排。导致代码出现一些问题。
private volatile int data = 0;
new Thead() {
public void run() {
data++;
}
}.start();
new Thread() {
public void run() {
while(data == 0) {
Thread.sleep(100);
}
}
}.start();
public class Kafka {
private volatile boolean running = true;
private void shutdown() {
running = false;
}
public static void main(String[] args) {
// 启动kafka,启动代码,不能直接让他退出
Kafka kafka = new Kafka();
while (kafka.running) {
Thread.sleep(1000);
}
}
}
加上volatile之后保证其可见性。
volatile关键字和有序性的关系,volatile是如何保证有序性的,如何避免发生指令重排。
Java中有一个happens-before原则:
**1.程序次序规则,**一个线程内,按照代码顺序,书写在前面的操作先行发生于书写在后面的操作。
2.锁定规则,一个unLock从挨揍先行发生于后面对同一个锁额lock操作。
3.volatile变量规则,对一个volatile变量的写操作先行发生于后面对这个变量的读操作。
4.传递规则,如果操作A先行发生于操作B,而操作B又先行发生于操作C,则可以得出操作A先行发生于操作C。
5.线程启动规则,对Thread对象的start()方法先行发生于此线程的每一个动作。
6.线程中断规则,对线程interrupt()方法的调用先行发生于被中断线程代码检测到中断事件的发生。
7.线程终结规则,线程中所有的操作都先行发生于线程的终止检测,我们可以通过Threa.join()方法结束、Thread.isAlive()的返回值手段检测到线程已经终止执行。
8.对象终结规则,一个对象的初始化完成先行发生于他的finalize()方法的开始。
不能够保证原子性,虽然有些极端情况有保证原子性的效果。
保证原子性,还是用sychronized,lock,加锁。