在JDK1.2 之前,Java的内存模型实现总是从主存(即共享内存)读取变量,是不需要进⾏特别的注意的。⽽在当前的 Java 内存模型中线程可以把变量保存本地内存(⽐如机器的寄存器)中,⽽不是直接在主存中进⾏读写。这就可能造成⼀个线程在主存中修改了⼀个变量的值,⽽另外⼀个线程还继续使⽤它在寄存器中的变量值的拷⻉,造成数据的不⼀致。
Java内存模型 JMM(Java Memory Moduel)
转载博客,详情请看
关键字volatile,指示 JVM,这个变量是不稳定的,每次使⽤它都到主存中进⾏读取。
保证共享变量可见性的两种方式:
(1)加锁
- 线程获得锁
- 清空当前工作内存
- 从主内存中拷贝共享变量最新值到工作内存成为副本
- 执行代码,如果更新了共享变量的值,刷新回主内存中
- 释放锁
(2)使用volatile修饰共享变量 - 线程a从主内存读取共享变量到对应的工作内存
- 对共享变量进行更改
- 线程b读取共享变量的值到对应的工作内存
- 线程a将修改后的值刷新到主内存,失效其他线程对共享变量的副本
- 线程b对共享变量进行操作时,发现已经失效,重新从主内存读取最新值,放入到对应工作内存
volatile与synchronized的区别
volatile只能修饰实例变量和类变量,而可以synchronized修饰实例方法和代码块。
volatile保证数据的可见性,但是不能保证数据的原子性,而synchronized两者都能保证【是一种互斥机制】。
volatile可以禁止指令重排序,可以检查使用单例双重检查中对象创建时执行代码的乱序问题。
1.死锁案例:volatile可以看做是轻量版的synchronized,volatile不保证原子性,但是如果对一个共享变量进行多线程的纯赋值操作,那么就可以使用volatile代替synchronized,因为赋值本身是原子性的,而volatile保证了可见性,即可以保证是线程安全的。
不同的线程占用对方需要的同步的资源不放弃, 都在等待对方放弃自己需要同步的资源,就形成了线程的死锁。如下面的例子:
public class DeadLock {
public static void main(String[] args) {
Service1 service1 = new Service1();
Service2 service2 = new Service2();
new Thread(()->{
synchronized (Service1.class){
service1.print();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Service2.class){
service1.say();
}
}
}).start();
new Thread(()->{
synchronized (Service2.class){
service2.print();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (Service1.class){
service2.say();
}
}
}).start();
}
}
class Service1{
public void print(){
System.out.println("我是线程1");
}
public void say(){
System.out.println("你好,线程2");
}
}
线程都为阻塞状态,无法继续。
2.写一个线程交替输出案例(A线程循环输出A,B线程循环输出B,出现ABABABAB… )交替打印’AB’
public class DemoABAB {
public synchronized void printA() throws InterruptedException {
System.out.print("A");
this.notify();
this.wait();
}
public synchronized void printB() throws InterruptedException {
System.out.print("B");
this.notify();
this.wait();
}
public static void main(String[] args) {
DemoABAB demoABAB = new DemoABAB();
new Thread(()->{
while (true){
try {
demoABAB.printA();
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
new Thread(()->{
while (true){
try {
Thread.sleep(1000);
demoABAB.printB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}



