共计 1930 个字符,预计需要花费 5 分钟才能阅读完成。
并发编程中,有三个非常重要的概念:
-
原子性:提供了一种互斥访问,同一时刻只能有一个线程对它进行操作
-
可见性:一个线程对主内存对修改可以及时的被其他线程观察到
-
有序性:一个线程观察其他线程的指令行执行顺序,由于指令重新排序的存在,该观察结果一般杂乱无序
而 volatile
关键字能够保证以下两点:
- 可见性(Visibility):
当一个线程修改了一个volatile
变量的值时,这个新值会立即被写入到主内存中,而不会先缓存在线程的本地内存中。同时,当其他线程读取这个volatile
变量时,它们会直接从主内存中读取最新的值,而不会使用本地缓存中的旧值。这样就确保了多个线程之间对共享变量的操作是可见的。
- 禁止指令重排序(Prevents Instruction Reordering):
volatile
关键字会禁止指令重排序优化,即在代码中volatile
变量之前的操作不会被重排序到volatile
变量之后,也不会被重排序到volatile
变量之后的操作之前。这样可以确保指令的顺序按照程序的顺序执行,避免了可能会影响多线程程序正确性的指令重排序。
需要注意的是,虽然volatile
关键字能够保证可见性和禁止指令重排序,但它并不能保证原子性,即不保证多线程操作的原子性。
另外,volatile
和 CAS(Compare and Swap,比较并交换)通常结合在一起使用,主要用于多线程编程中实现原子操作,特别是在并发环境下确保数据的一致性和可见性。
关于 CAS,java.util.concurrent.atomic
包提供了一系列原子操作的类,其中就包括了基于 CAS 的实现。最常见的就是java.util.concurrent.atomic.AtomicInteger
、java.util.concurrent.atomic.AtomicLong
等,它们提供了诸如compareAndSet()
等方法,用于在不使用锁的情况下进行原子性的操作。CAS 的使用可以提高并发性能,因为它避免了使用锁带来的线程阻塞和上下文切换。
使用 volatile 和 CAS 来实现一个线程安全的计数器:
import java.util.concurrent.atomic.AtomicInteger; | |
public class VolatileCASExample { | |
private volatile int counter = 0; | |
public int getCounter() { | |
return counter; | |
} | |
public void increment() { | |
int currentValue; | |
int newValue; | |
do { | |
currentValue = counter; // 获取当前值 | |
newValue = currentValue + 1; // 计算新值 | |
} while (!compareAndSwap(currentValue, newValue)); // CAS 操作,如果失败则重试 | |
} | |
private synchronized boolean compareAndSwap(int expect, int update) { | |
if (counter == expect) { // 检查当前值是否与期望值相等 | |
counter = update; // 如果相等,则更新为新值 | |
return true; | |
} | |
return false; | |
} | |
public static void main(String[] args) { | |
final VolatileCASExample example = new VolatileCASExample(); | |
final AtomicInteger total = new AtomicInteger(0); | |
// 创建多个线程并发增加计数器值 | |
Runnable task = () -> { | |
for (int i = 0; i < 1000; i++) { | |
example.increment(); | |
total.incrementAndGet(); | |
} | |
}; | |
// 启动多个线程 | |
Thread[] threads = new Thread[10]; | |
for (int i = 0; i < threads.length; i++) { | |
threads[i] = new Thread(task); | |
threads[i].start(); | |
} | |
// 等待所有线程完成 | |
for (Thread thread : threads) { | |
try { | |
thread.join(); | |
} catch (InterruptedException e) { | |
e.printStackTrace(); | |
} | |
} | |
// 输出计数器的最终值 | |
System.out.println("Final counter value: " + example.getCounter()); | |
System.out.println("Total increments: " + total.get()); | |
} | |
} |
提醒:本文发布于461天前,文中所关联的信息可能已发生改变,请知悉!