zoukankan      html  css  js  c++  java
  • Java中初级数值类型的大小, volatile和包装类wrapped type的比较

    Java中的初级数值类型

    Java是静态类型语言, 所有的变量必须先声明再使用. 其初级类型一共8种:

    • boolean: 数据只包含1bit信息, 但是占空间为8-bit, 默认值为false
    • byte: 8-bit 带符号补码型整数, 取值 -128 ~ 127. 使用于一些对内存空间敏感的大型数组.
    • char: 16-bit单Unicode字符, 最小值也是默认值, 为'u0000'(或0), 最大值为'uFFFF'(或65,535).
    • short: 16-bit 带符号补码型整数, 取值 -32,768 ~ 32,767, 用途同上
    • int: 32-bit 带符号补码型整数, 取值 -231 ~ 231 - 1,  这个值是 2,147,483,648(约21.4亿). 自Java8开始, 可以使用int类型来表示一个无符号32-bit整数, 表示 0 ~ 232 的数值, 具体实现在Integer类中.
    • long: 64-bit 带符号补码型整数, 取值 -263 ~ 263 - 1, 自Java8开始, 也可以通过long类型表示一个无符号64-bit整数, 具体实现在Long类中.
    • float: 单精度32-bit IEEE754浮点数.
    • double: 双精度64-bit IEEE754浮点数.

    关于32位JVM和64位JVM

    无论是32-bit JVM, 还是 64-bit JVM, 以上数值所占空间都是一样的. 在32位JVM和64位JVM上唯一不同的就是引用(reference)的空间大小. 这是关于32位系统和64位系统的一个误区. 在Oracle Java6 update23以及之后, 对于heap大小在32GB以下的JVM, 默认会使用32-bit的引用. 所以严格来讲, 只有heap在32GB以上的64-bit的JVM, 才会在引用这个类型上有区别.

    关于初级数值类型赋值的原子性

    对于所有的JVM, 除了longdouble, 其他初级数值类型的赋值都是保证原子性的. 在64-bit JVM中, 因为64-bit的引用类型其赋值一定是原子的, 所以对long和double的赋值也应当是原子的.

    多线程下的可见度

    但即便是保证原子性, 仍然不能保证多线程环境下各线程对数值变化的"可见度", 因为线程在内存中会生成"影子变量", 对一个变量的赋值并不能保证将其写回主内存, 除非在主内存更新时被自动更新. 如果你希望线程间所读取的数值保持一致, 你必须以某种同步壁垒的形式来访问线程间共享的状态. 例如volatile关键字或锁.

    Volatile 关键字

    可以参考这篇文章, 介绍得非常详细: http://tutorials.jenkov.com/java-concurrency/volatile.html

    volatile关键字用于将变量标识为"存储于主内存". 更精确些, 就是每一次读取变量值的时候, 都必须从计算机的主内存中读取, 而不能从CPU的缓存, 每一次写变量值的时候, 也必须写到主内存, 而不是CPU的缓存.

    Volatile附带的"变化可见度"特性

    • 如果Thread A 写了一个volatile变量 并且Thread B读取了这个volatile变量, 那么所有在写入这个变量前对A"可见"的变量, 在B读取这个变量之后, "变化"变得对B可见.
    • 如果Thread A读取了一个volatile变量, 那么所有在A读取这个变量之前对A"可见"的变量, 在读取后会从主内存中重新读取.

    编译时对volatile的处理, 在JDK5以及之后是不同的, 在JDK4时, 对volatile变量的读写与对其他变量的读写指令, 在编译优化阶段可能会被调换顺序, 在JDK5之后保证了发生在volatile变量之前的读写, 不会被调整到volatile变量的读写之后.

    JDK5以及之后的顺序保证(Happens-Before Guarantee)

    • 如果代码中对某个变量的读取和写入发生在对volatile变量的写入之前, 那么编译后这个读写操作保证不会被调整到对volatile的写入之后. 注意这仅仅是保证发生在volatile写入之前的操作不会放到后面, 但是不能保证volatile写入之后的操作不会被放到前面.
    • 如果代码中对某个变量的读取和写入发生在对volatile变量的读取之后, 那么编译后这个读写操作保证不会被调整到对volatile的读取之前. 注意这也不能保证volatile读取之前的操作不会被放到后面.

    在有写入的场景下, volatile是不够的

    需要用synchronized来保证写入不会互相覆盖, 或者使用AtomLong 或 AtomicReference 这样的原子操作类型数据.

    包装类的实例之间比较不能直接用 == 

        public static void main(String[] args) {
            // TODO Auto-generated method stub
            Integer a = new Integer(1);
            Integer b = new Integer(1);
            int c=1;
            Integer e = 1;
            System.out.println("a==b:"+(a==b));
            System.out.println("a==c:"+(a==c));
            System.out.println("a==e:"+(a==e));
            System.out.println("c==e:"+(c==e));
        }

    结果:
    a==b:false
    a==c:true
    a==e:false
    c==e:true

  • 相关阅读:
    解决 Windows 下的 :所选择的任务“{0}”不再存在。若要查看当前任务,请单击“刷新”。
    学习编译更好的 DAO 的技巧
    宇宙的起源演讲全文(斯蒂芬·霍金)
    java對象序列化的兩種使用方法
    Ubuntu硬盘安装与配置(3D效果)
    java異常處理
    [zt]JDBC对数据库的事务操作
    [zt]spring本地事务与JTA事务实现解析
    Debian溫習
    在oracle中增大session數量
  • 原文地址:https://www.cnblogs.com/milton/p/4562339.html
Copyright © 2011-2022 走看看