volatile关键字和AtomicInteger

简介: volatile关键字和AtomicInteger

在Java中,线程部分是一个重点,本篇文章说的JUC也是关于线程的。JUC就是java.util .concurrent工具包的简称。这是一个处理线程的工具包,JDK 1.5开始出现的。下面一起来看看它怎么使用。


一、volatile关键字与内存可见性


1、内存可见性:

先来看看下面的一段代码:




上图中这段代码很简单,就是一个ThreadDemo类继承Runnable创建一个线程。它有一个成员变量flag为false,然后重写run方法,在run方法里面将flag改为true,同时还有一条输出语句。然后就是main方法主线程去读取flag。如果flag为true,就会break掉while循环,否则就是死循环。按道理,下面那个线程将flag改为true了,主线程读取到的应该也是true,循环应该会结束。运行结果



从上图中可以看到,该程序并没有结束,也就是死循环。说明主线程读取到的flag还是false,可是另一个线程明明将flag改为true了,而且打印出来了,这是什么原因呢?这就是内存可见性问题。


内存可见性问题:当多个线程操作共享数据时,彼此不可见。


要解决这个问题,可以加锁。如下:


加了锁,就可以让while循环每次都从主存中去读取数据,这样就能读取到true了。但是一加锁,每次只能有一个线程访问,当一个线程持有锁时,其他的就会阻塞,效率就非常低了。不想加锁,又要解决内存可见性问题,那么就可以使用volatile关键字。



2、volatile关键字:


用法:

volatile关键字:当多个线程操作共享数据时,可以保证内存中的数据可见。用这个关键字修饰共享数据,就会及时的把线程缓存中的数据刷新到主存中去,也可以理解为,就是直接操作主存中的数据。所以在不使用锁的情况下,可以使用volatile。如下:


这样就可以解决内存可见性问题了。


volatile和synchronized的区别:

volatile不具备互斥性(当一个线程持有锁时,其他线程进不来,这就是互斥性)。

volatile不具备原子性。



二、原子性


1、理解原子性:


上面说到volatile不具备原子性,那么原子性到底是什么呢?先看如下代码:

 


这段代码就是在run方法里面让i++,然后启动十个线程去访问。看看结果:

 

 

可以发现,出现了重复数据。明显产生了多线程安全问题,或者说原子性问题。所谓原子性就是操作不可再细分,而i++操作分为读改写三步,如下:


所以i++明显不是原子操作。上面10个线程进行i++时,内存图解如下:


看到这里,好像和上面的内存可见性问题一样。是不是加个volatile关键字就可以了呢?其实不是的,因为加了volatile,只是相当于所有线程都是在主存中操作数据而已,但是不具备互斥性。比如两个线程同时读取主存中的0,然后又同时自增,同时写入主存,结果还是会出现重复数据。


2、原子变量:

JDK 1.5之后,Java提供了原子变量,在java.util.concurrent.atomic包下。原子变量具备如下特点:


有volatile保证内存可见性。

用CAS算法保证原子性。

3、CAS算法:

CAS算法是计算机硬件对并发操作共享数据的支持,CAS包含3个操作数:


内存值V

预估值A

更新值B

当且仅当V==B时,才会把B的值赋给V,即V = B,否则不做任何操作。就上面的i++问题,CAS算法是这样处理的:首先V是主存中的值0,然后预估值A也是0,因为此时还没有任何操作,这时V=B,所以进行自增,同时把主存中的值变为1。如果第二个线程读取到主存中的还是0也没关系,因为此时预估值已经变成1,V不等于B,所以不进行任何操作。


4、使用原子变量改进i++问题:



定义:AtomicInteger是一个提供原子操作的Integer类,通过线程安全的方式操作加减。

使用场景 :适合高并发情况下的使用

AtomicInteger是在使用非阻塞算法实现并发控制,在一些高并发程序中非常适合,但并不能每一种场景都适合,不同场景要使用使用不同的数值类。


注意:高并发的情况下,i++无法保证原子性,往往会出现问题,所以引入AtomicInteger类。



原子变量用法和包装类差不多,如下:


相关文章
|
6月前
|
缓存 编译器
volatile关键字
volatile关键字
|
6月前
|
缓存 编译器 C语言
一起来探讨volatile关键字
在C语言中,volatile是一个关键字,用于告诉编译器不要对被声明为volatile的变量做优化,以确保每次对该变量的读写都直接操作内存。
|
缓存 安全 Java
【volatile关键字】
【volatile关键字】
|
存储 缓存 Java
volatile 关键字说明
volatile 关键字说明
51 0
|
3月前
|
缓存 Java 编译器
关键字: volatile详解
综上所述,`volatile`关键字是Java中实现轻量级同步的一个重要手段,主要用于确保变量的跨线程可见性,但并不保证操作的原子性。在多线程编程的过程中,合理地选择和使用 `volatile`关键字,对于提高程序的正确性和性能都至关重要。
40 0
|
安全 Java 编译器
volatile 与 synchronized 关键字的区别?
volatile 与 synchronized 关键字的区别?
54 0
|
缓存 安全 Java
Java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic
Java并发编程中的四个关键字:ThreadLocal、Volatile、Synchronized和Atomic
266 0
|
存储 Java
浅谈Volatile关键字
该篇文章用来总结笔者对于Volatile关键字的理解,并不会太过深入的探讨。
135 0
浅谈Volatile关键字
java多线程关键字volatile、lock、synchronized
volatile写和volatile读的内存语义: 1. 线程A写一个volatile变量,实质上是线程A向接下来将要读这个volatile变量的某个线程发出了(其对共享变量所在修改的)消息。 2. 线程B读一个volatile变量,实质上是线程B接收了之前某个线程发出的(在写这个volatile变量之前对共享变量所做修改的)消息。 3. 线程A写一个volatile变量,随后线程B读这个volatile变量,这个过程实质上是线程A通过主内存向线程B发送消息。
287 0
|
SQL 缓存 安全
深入理解volatile关键字
深入理解volatile关键字
194 0
深入理解volatile关键字