一、每个Java线程都有⾃⼰的⼯作内存
操作数据,⾸先从主内存中读,得到⼀份拷⻉,操作完毕后再写回到主内存。
不同的线程间⽆法访问对⽅的⼯作内存,线程间的通信 (传值)必须通过主内存来完成
二、不同的线程间⽆法访问对⽅的⼯作内存
package thread;
import java.util.concurrent.TimeUnit;
/**
* volitale关键字是Java提供的⼀种轻量级同步机制。
* 它能够保证可⻅性和有序性
* 但是不能保证原⼦性
* 禁⽌指令重排
*/
class MyData {
int number = 0;
// volatile int number = 0;
public void setTo60() {
this.number = 60;
}
}
public class VolatileDemo {
public static void main(String[] args) {
volatileVisibilityDemo();
}
// volatile可以保证可⻅性,及时通知其它线程主物理内存的值已被修改
private static void volatileVisibilityDemo() {
System.out.println("可⻅性测试");
MyData myData = new MyData();//资源类
// 启动⼀个线程操作共享数据
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 执⾏");
try {
TimeUnit.SECONDS.sleep(3);
// 更新number的值
myData.setTo60();
System.out.println(Thread.currentThread().getName() + "\t 更新number值: " + myData.number);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}, "ThreadA").start();
// main线程,获取共享变量改变后的结果
while (myData.number == 0) {
// main线程持有共享数据的拷⻉,⼀直为0
// 若number的值一直为0,说明main线程没有得到共享变量改变值的通知
}
System.out.println(Thread.currentThread().getName() + "\t main获取 number值: " + myData.number);
}
}
虽然⼀个线程把number修改成了60,但是main线程持有的仍然是最开始的0,所以⼀直循环,程序不会结束。
三、如果对变量添加了volatile修饰
package thread;
import java.util.concurrent.TimeUnit;
/**
* volitale关键字是Java提供的⼀种轻量级同步机制。
* 它能够保证可⻅性和有序性
* 但是不能保证原⼦性
* 禁⽌指令重排
*/
class MyData {
// int number = 0;
volatile int number = 0;
public void setTo60() {
this.number = 60;
}
}
public class VolatileDemo {
public static void main(String[] args) {
volatileVisibilityDemo();
}
// volatile可以保证可⻅性,及时通知其它线程主物理内存的值已被修改
private static void volatileVisibilityDemo() {
System.out.println("可⻅性测试");
MyData myData = new MyData();//资源类
// 启动⼀个线程操作共享数据
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 执⾏");
try {
TimeUnit.SECONDS.sleep(3);
// 更新number的值
myData.setTo60();
System.out.println(Thread.currentThread().getName() + "\t 更新number值: " + myData.number);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}, "ThreadA").start();
// main线程,获取共享变量改变后的结果
while (myData.number == 0) {
// main线程持有共享数据的拷⻉,⼀直为0
// 若number的值一直为0,说明main线程没有得到共享变量改变值的通知
// System.out.println(Thread.currentThread().getName() + "\t 等待。。。 ");
}
System.out.println(Thread.currentThread().getName() + "\t main获取 number值: " + myData.number);
}
}
可⻅某个线程对number的修改,会⽴刻反映到主内存上。
每个线程是独立的,但是线程对变量的所有操作都必须在拷贝到自己的工作内存中进行,而不能直接读写主内存中的变量。
不同线程之间也无法直接访问对方工作内存中的变量。
当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去主内存中读取新值,所以可见性是立即可见的意思。
如果没有加volatile,线程A修改了,还没来得及写回主内存,另一个线程也要修改,但是他没保证可见性,去主内存没拿到线程A修改的最新的值,就会出现问题。
四、System.out.println()有锁
即使没有volitale修饰
package thread;
import java.util.concurrent.TimeUnit;
/**
* volitale关键字是Java提供的⼀种轻量级同步机制。
* 它能够保证可⻅性和有序性
* 但是不能保证原⼦性
* 禁⽌指令重排
*/
class MyData {
int number = 0;
// volatile int number = 0;
public void setTo60() {
this.number = 60;
}
}
public class VolatileDemo {
public static void main(String[] args) {
volatileVisibilityDemo();
}
// volatile可以保证可⻅性,及时通知其它线程主物理内存的值已被修改
private static void volatileVisibilityDemo() {
System.out.println("可⻅性测试");
MyData myData = new MyData();//资源类
// 启动⼀个线程操作共享数据
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + "\t 执⾏");
try {
TimeUnit.SECONDS.sleep(3);
// 更新number的值
myData.setTo60();
System.out.println(Thread.currentThread().getName() + "\t 更新number值: " + myData.number);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}, "ThreadA").start();
// main线程,获取共享变量改变后的结果
while (myData.number == 0) {
// main线程持有共享数据的拷⻉,⼀直为0
// 若number的值一直为0,说明main线程没有得到共享变量改变值的通知
System.out.println(Thread.currentThread().getName() + "\t 等待。。。 ");
}
System.out.println(Thread.currentThread().getName() + "\t main获取 number值: " + myData.number);
}
}
结果
原因 synchronized