脑海第一感觉 static int 声明的属性一定是非线程安全的. int 直接声明的属性难道也是非线程安全吗?(疑问).
通过题面意思就能感觉到面试官的意图, 他就是想让你说是非线程安全的. 然后他好问为什么. 结果我直接说不知道. 说实话真拿不准, 于是自己通过实践验证得出了一些结论并记录下来. 加申印象.
- private static int value = 1;
- private int value = 1;
以下想通过实践证明几点:
1. 两种声明方式是否线程安全.
2. 总结两种方式的区别.
第一两种声明方式是否线程安全.
证明 1:private static int value = 1; 非线程安全
- /**
- * 证明 static int 声明属性为非线程安全的类
- */
- class TTT {
- static int value = 1; // 注释 1:Integer value = new Integer(1) 同样
- public int get1() throws InterruptedException {
- Thread.sleep(10); // 注释 2: 值越大重复值越多
- return value++;
- }
- }
- /**
- * 测试类
- */
- public class Test2 {
- public static void main(String[] args) {
- TTT t = new TTT(); // 注释 3: 实例化一个对象, 并通过多个线程调用 get1 方法.
- for(int i=1; i<=1000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println(t.get1());
- } catch (Exception e) {
- }
- }
- }).start();
- }
- }
- }
期望结果: 1 - 1000
实际结果: 1 - x (<1000) 实际输出结果中存在重复值
将已上代码片段稍作调整再次验证.
注释 3 处, 实例化对象挪到线程 run 方法体内
- /**
- * 证明 static int 声明属性为非线程安全的类
- */
- class TTT {
- static int value = 1; // 注释 1:Integer value = new Integer(1) 同样
- public int get1() throws InterruptedException {
- Thread.sleep(10); // 注释 2: 值越大重复值越多
- return value++;
- }
- }
- /**
- * 测试类
- */
- public class Test2 {
- public static void main(String[] args) {
- for(int i=1; i<=1000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- TTT t = new TTT(); // 注释 3: 实例化 1000 个对象, 并调用 get1 方法.
- try {
- System.out.println(t.get1());
- } catch (Exception e) {
- }
- }
- }).start();
- }
- }
- }
期望结果: 1 - 1000
实际结果: 1 - x (<1000) 实际输出结果中存在重复值
结论: 已上两种情况相同针对 private static int value = 1; 都是非线程安全的.
那么都知道通过 synchronized 关键字可以将 get1 方法改为线程安全的. 分别在两段代码片段中的 get1 方法加上 synchronized 关键字, 但是结果却又不同了
第一段代码 实例化一个对象, 并通过 1000 个线程调用 get1 方法, synchronized 关键字起作用的.
第二段代码 通过 1000 个线程实例化 1000 个对象, 并调用 get1 方法, synchronized 关键字不起作用.
补充: synchronized 关键字在多线程情况下针对同一个实例 (对象 Object) 是起作用的.
证明 2:private int value = 1; 非线程安全
- /**
- * 证明 int 声明属性为非线程安全的类
- */
- class TT {
- private int value = 1; //Integer value = new Integer(1) 同样
- public int get1() throws Exception {
- Thread.sleep(10); // 值越大重复值越多
- return value++;
- }
- }
- /**
- * 测试类
- */
- public class Test {
- public static void main(String[] args) throws Exception {
- TT t = new TT(); // 注释 3: 实例化一个对象, 并通过多个线程调用 get1 方法.
- for(int i=1; i<=1000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- try {
- System.out.println(t.get1());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- }
- }
- }
期望结果: 1 - 1000
实际结果: 1 - x (<1000) 实际输出结果中存在重复值
同样将已上代码片段稍作调整再次验证.
注释 3 处, 实例化对象挪到线程 run 方法体内
- /**
- * 证明 int 声明属性为非线程安全的类
- */
- class TT {
- private int value = 1; //Integer value = new Integer(1) 同样
- public int get1() throws Exception {
- Thread.sleep(10); // 值越大重复值越多
- return value++;
- }
- }
- /**
- * 测试类
- */
- public class Test {
- public static void main(String[] args) throws Exception {
- for(int i=1; i<=1000; i++) {
- new Thread(new Runnable() {
- @Override
- public void run() {
- TT t = new TT(); // 注释 3: 实例化 1000 个对象, 并调用 get1 方法.
- try {
- System.out.println(t.get1());
- } catch (Exception e) {
- e.printStackTrace();
- }
- }
- }).start();
- }
- }
- }
实际结果: 输出的全部是 1
结论: 针对 private int value = 1;
第一段代码 实例化一个对象, 并通过 1000 个线程调用 get1 方法, value 值非线程安全.
第二段代码 通过 1000 个线程实例化 1000 个对象, 并调用 get1 方法, value 值线程安全.
补充: 在多线程情况下针对同一个实例 (对象 Object) 内的基础类型声明的属性 进行调用是非线程安全的.
总结两种方式的区别.
静态属性相对于类 (class) 是非线程安全的. 如上结论无论实例化一个对象, 并通过多线程调用方法. 还是通过多线程实例化多个对象, 调用方法结果是一样的.
一般属性相对于对象 (object) 是非线程安全的. 如上结论, 实例化一个对象, 并通过多个线程调用方法获取属性值, 值是不可靠的. 而实通过线程实例化多个对象, 并调用方法获取属性值, 值是可靠的.
具体理论可参考 jvm 实战第二章内存管理.
来源: http://www.jianshu.com/p/b4f67085f789