Java语言规范(Java Language Specification)的官方解释:
- If a field is declared static, there exists exactly one incarnation of the field, no matter how many instances (possibly zero) of the class may eventually be created.
- A field may be declared volatile, in which case the Java Memory Model ensures that all threads see a consistent value for the variable。
还是迷糊,求解答。主要困惑是:
既然static保证了唯一性,那么他对多个线程来说都是可见的啊,volatile保证了线程之间的可见性,那么修改的时候只要是原子操作,那么就会保证它的唯一性了吧。这两个在我理解上我觉得差不多。
需要了解的知识
static指的是类的静态成员,实例间共享
volatile跟Java的内存模型有关,线程执行时会将变量从主内存加载到线程工作内存,建立一个副本,在某个时刻写回。valatile指的每次都读取主内存的值,有更新则立即写回主内存。
理解了这两点,逐句再来解释你的困惑:
“既然static保证了唯一性”:static保证唯一性,指的是static修饰的静态成员变量是唯一的,多个实例共享这唯一一个成员。
“那么他对多个线程来说都是可见的啊”:这里,static其实跟线程没太大关系,应该说对多个对象实例是可见的。你说对多个线程可见,虽然没什么毛病,因为静态变量全局可见嘛,但是把这个理解转到线程的上线文中是困惑的起因。
“volatile保证了线程之间的可见性”:因为线程看到volatile变量会去读取主内存最新的值,而不是自个一直在那跟内部的变量副本玩,所以保证了valatile变量在各个线程间的可见性。
“那么修改的时候只要是原子操作,那么就会保证它的唯一性了吧”:此时你说的“唯一性”,指的是各个线程都能读取到唯一的最新的主内存变量,消除了线程工作内存加载变量副本可能带来的线程之间的“不唯一性”。这里“唯一性”的含义跟第一句说的“唯一性”是不一样的。
“这两个在我理解上我觉得差不多。”:其实解决问题的“场景”是完全不一样的。
造成理解困惑最大的原因在于,这两个场景略有类似,以致混淆了:
场景1:各个类的实例共享唯一一个类静态变量
场景2:各个线程共同读取唯一的最新的主内存变量的值
代码示例:
假如没有 static 关键字,那么使用同一个对象,才是可见的,如果不是同一个对象那么值不一致,除非加上 static 关键字,那么 Test1 种的变量才会同步:
public class Test1 {
public volatile int s1 = 0;
}
package com.xcewell.oms.api;
public class Test2 {
public static void main(String[] args) {
Test1 test1 = new Test1();
new Thread() {
@Override
public void run() {
try {
while (true) {
System.out.println(test1.s1);
test1.s1++;
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread() {
@Override
public void run() {
try {
while (true) {
System.out.println("线程2:" + test1.s1);
Thread.sleep(3000);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}.start();
}
}
结果: