一、每个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