深入理解 Java 中的整数类型:int、Integer 和 AtomicInteger

54 阅读9分钟

Java 中的整数类型好比高楼的根基,坚实而基础,为整个程序的稳定和高效运算打下了不可或缺的基石。正如万丈高楼平地起,没有坚固的砖块,就没有屹立不倒的大厦。

一、基本数据类型 int

int 类型是 Java 编程中最常用的数据类型之一,它的设计兼顾了性能和广泛的应用范围,使得它在各种编程任务中都非常适用。

jbsjlx.jpg

1.1 定义与范围

  • int 是一种 32 位有符号的整数类型,意味着它可以用来存储一个介于 -2^312^31-1 之间的整数值。

  • 具体来说,int 类型可以表示的数值范围是从 -2,147,483,6482,147,483,647

1.2 内存占用

  • int 类型的变量在内存中占用 32 位(4 字节)的空间。这是因为 int 类型被设计为固定长度,以确保不同平台和不同架构的计算机之间数据的一致性。

1.3 性能优势

  • 访问速度:由于 int 是基本数据类型,它比包装类 Integer 的对象表示更接近硬件层面,因此访问速度更快。

  • 存储效率int 直接存储在栈上或内存的连续空间内,不需要额外的对象头信息,因此在内存使用上更为紧凑。

  • 缓存友好:基本数据类型的操作通常更易于被 CPU 缓存优化,因为它们是简单的数据而非复杂的对象。

1.4 使用场景

image.png

二、包装类 Integer

包装类(Wrapper Class)是 Java 中的一个概念,用于将基本数据类型包装成对象。这样做的好处是可以使用对象的特性来操作基本数据类型,比如把它们存储在集合中或传递给需要对象参数的方法。

bzl.jpg

2.1 包装类的概念

  • 在 Java 中,基本数据类型不能直接作为对象使用,因为它们没有对象的属性和方法。包装类提供了一种机制,将基本数据类型转换成对象,这样就可以使用面向对象的特性。

2.2 Integer 类的特点

  • Integer 是 int 基本数据类型的包装类,它提供了一个 int 类型的字段来存储数据。

  • Integer 类实现了 Comparable 接口,可以比较两个 Integer 对象的大小。

  • Integer 类是不可变的,一旦创建了 Integer 对象,它的值就不能被更改。

2.3 自动装箱与拆箱

  • 自动装箱(Autoboxing):在 Java 5 及以后的版本中,可以将基本数据类型的 int 直接赋值给 Integer 类型的对象,编译器会自动将 int 转换为 Integer 对象。

  • 自动拆箱(Unboxing):相反,也可以将 Integer 对象直接赋值给 int 类型的变量,编译器会自动将 Integer 对象转换为 int 类型。

2.4 缓存机制(Integer.valueOf 和 Integer.intValueOf)

  • Integer 类型有一个缓存机制,用于提高性能。Integer.valueOf(int i) 方法会使用一个缓存好的 Integer 对象,如果输入的 int 值在 -128 到 127 之间(包含),则会直接返回一个缓存中的对象,而不是每次都创建一个新的对象。

  • 这个缓存机制是通过 IntegerCache 实现的,它是 Integer 类的一个内部类。

2.5 与 int 的相互转换

  • 从 Integer 到 int:可以通过调用 Integer 对象的 intValue() 方法来获取其对应的 int 值。

  • 从 int 到 Integer:可以直接将 int 类型的值赋给 Integer 类型的对象,或者使用 Integer.valueOf(int i) 静态方法来创建一个新的 Integer 对象。

int num = 100; // 基本数据类型
Integer intObj = Integer.valueOf(num); // 自动装箱
System.out.println(intObj); // 输出 Integer 对象

int numInt = intObj.intValue(); // 自动拆箱
System.out.println(numInt); // 输出 int 值

三、原子类 AtomicInteger

原子类 AtomicInteger 是 Java 中一个非常关键的并发工具,它提供了一种在多线程环境下对整数进行原子操作的方式。

yzl.jpg

3.1 原子操作的概念

原子操作是指在一个步骤中完成的操作,这个操作要么完全执行,要么完全不执行,不会出现中间状态,从而保证了在多线程环境中数据的一致性和线程安全性

3.2 AtomicInteger 的用途

AtomicInteger 主要用于多线程环境中,当需要对一个整数进行自增、自减或其他数值操作时,能够保证这些操作是原子性的。这使得 AtomicInteger 成为实现计数器或在没有线程间通信时控制变量更新的理想选择

3.3 基本操作:get、set、incrementAndGet 等

AtomicInteger的官方文档参考AtomicInteger

它的一些核心方法如下:

intaccumulateAndGet(int x, IntBinaryOperator accumulatorFunction)使用将给定函数应用于当前值和给定值的结果原子更新当前值,返回更新后的值。
intaddAndGet(int delta)将给定的值原子地添加到当前值。
booleancompareAndSet(int expect, int update)如果当前值 ==为预期值,则将该值原子设置为给定的更新值。
intdecrementAndGet()原子减1当前值。
doubledoubleValue()返回此值 AtomicInteger为 double一个宽元转换后。
floatfloatValue()返回此值 AtomicInteger为 float一个宽元转换后。
intget()获取当前值。
intgetAndAccumulate(int x, IntBinaryOperator accumulatorFunction)使用给定函数应用给当前值和给定值的结果原子更新当前值,返回上一个值。
intgetAndAdd(int delta)将给定的值原子地添加到当前值。
intgetAndDecrement()原子减1当前值。
intgetAndIncrement()原子上增加一个当前值。
intgetAndSet(int newValue)将原子设置为给定值并返回旧值。
intgetAndUpdate(IntUnaryOperator updateFunction)用应用给定函数的结果原子更新当前值,返回上一个值。
intincrementAndGet()原子上增加一个当前值。
intintValue()将 AtomicInteger的值作为 int 。
voidlazySet(int newValue)最终设定为给定值。
longlongValue()返回此值 AtomicInteger为 long一个宽元转换后。
voidset(int newValue)设置为给定值。
StringtoString()返回当前值的String表示形式。
intupdateAndGet(IntUnaryOperator updateFunction)使用给定函数的结果原子更新当前值,返回更新的值。
booleanweakCompareAndSet(int expect, int update)如果当前值 ==为预期值,则将值设置为给定更新值。

3.4 线程安全特性

AtomicInteger 的实现基于 volatile 关键字和 Unsafe 类提供的硬件级别的原子操作。volatile 确保了变量的内存可见性,而 Unsafe 类的 compareAndSwapInt 方法则实现了无锁的原子操作。

3.5 与 synchronized 的比较

synchronized 相比,AtomicInteger 使用了乐观锁策略,它不会让线程在获取不到锁时阻塞,而是通过循环尝试的方式直到操作成功。

这种机制在竞争不激烈的情况下通常比 synchronized 有更好的性能表现。

然而,世事无绝对,在高竞争环境下,synchronized 可能因为其锁机制的优化(如偏向锁、轻量级锁等)而表现更好。

四、int、Integer 和 AtomicInteger 的比较

特性/类型intIntegerAtomicInteger
使用场景通常用于表示数值,适用于简单的数值计算和数组索引是 int 的包装类,适用于需要使用对象特性的场合,如存储在集合中或作为方法的参数用于多线程环境中的原子类,适用于需要原子操作的计数器或其他数值,如实现锁和同步机制
性能考量作为基本数据类型,在性能上通常是最高的,直接存储数值,没有额外的对象开销性能相对较低,涉及对象的创建和垃圾回收,频繁的自动装箱拆箱可能带来性能开销在多线程环境下性能较高,避免使用传统同步机制,但单线程性能可能不如 int
线程安全性基本数据类型,不存在线程安全问题,但多线程环境可能产生竞争条件不可变的,创建后值不可变,线程安全;但多个线程修改同一引用需外部同步提供原子操作,无需额外同步即可保证线程安全
内存占用占用固定的内存空间(32位系统上为4字节)除了存储值,还包含对象头和其他元数据,占用更多内存包含值和实现原子操作的额外状态或控制信息,可能比 Integer 占用更多内存

五、总结

总结一下今天的主角们:intIntegerAtomicInteger

  1. int:它就像是一位老派的银行家,穿着经典的三件套西装,一丝不苟地管理着数字世界。它快速、精确,但略显古板,不懂变通。
  2. Integer:这位是银行家的侄子,穿着时尚,喜欢社交。他可以轻松地融入各种派对(对象集合),并且总是乐于接受新的角色(方法参数)。
  3. AtomicInteger:现在,这位是穿着高科技装备的忍者,悄无声息地在多线程的高楼大厦间穿梭。它行动迅速,总是在不被发现的情况下完成任务。

在性能的赛道上,int 以其轻装上阵的优势一马当先。Integer 虽然华丽,但身上的装饰(对象头信息)让它跑得慢了一些。而 AtomicInteger,虽然装备了最尖端的科技,但在单线程的直线赛道上,它那些复杂的装备反而成了累赘。

在线程安全的堡垒中,int 就像一座没有守卫的金库,容易受到攻击。Integer 虽然坚固,但如果多条线程都想进去,还是需要外部的守卫(同步)来维持秩序。AtomicInteger 则是那座配备了最先进安全系统的金库,即使在最混乱的场面中也能保持安全。

最后,让我们以一个笑话结束这场舞会:为什么 AtomicInteger 从来不去健身房?因为它已经足够核心了!

希望本文对您有所帮助。如果有任何错误或建议,请随时指正和提出。

同时,如果您觉得这篇文章有价值,请考虑点赞和收藏。这将激励我进一步改进和创作更多有用的内容。

感谢您的支持和理解!