美文网首页程序员
1.java面试问题 之 基础类型属性(int)是否线程安全?

1.java面试问题 之 基础类型属性(int)是否线程安全?

作者: 黄殿玺 | 来源:发表于2020-02-09 09:31 被阅读0次

    脑海第一感觉 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)内的基础类型声明的属性 进行调用是非线程安全的。

    总结两种方式的区别。
    1. 静态属性相对于类(class)是非线程安全的。如上结论无论实例化一个对象,并通过多线程调用方法。还是通过多线程实例化多个对象,调用方法结果是一样的。
    2. 一般属性相对于对象(object)是非线程安全的。如上结论,实例化一个对象,并通过多个线程调用方法获取属性值,值是不可靠的。而实通过线程实例化多个对象,并调用方法获取属性值,值是可靠的。

    具体理论可参考jvm实战第二章内存管理。

    相关文章

      网友评论

        本文标题:1.java面试问题 之 基础类型属性(int)是否线程安全?

        本文链接:https://www.haomeiwen.com/subject/vupwthtx.html