我们来看一段简单的代码:
public class marry {
static int money = 0;
public static void main(String[] args) {
new Thread(() ->{
while (money < 220000){
// System.out.println("现在的钱有"+ money);
}
System.out.println("可以结婚了");
}).start();
new Thread(() -> {
System.out.println("给我5年,存22w");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
money = 220000;
System.out.println("存够钱啦");
}).start();
}
}
结果为:

问题在于money发生改变时,第一个线程无法获得最新的money。
1,堆内存是唯一的,每一个线程都有自己的线程栈。
2 ,每一个线程在使用堆里面变量的时候,都会先拷贝一份到变量的副本中。
3 ,在线程中,每一次使用是从变量的副本中获取的。
要解决以上问题:
第一种方法:
Volatile关键字,能够强制线程每次在使用的时候,都会看一下共享区域最新的值。
第二种方法:
synchronized解决 :
1 ,线程获得锁
2 ,清空变量副本
3 ,拷贝共享变量最新的值到变量副本中
4 ,执行代码
5 ,将修改后变量副本中的值赋值给共享数据
6 ,释放锁
但是volatile关键字不能保证原子性!但加锁也可以。
原子性 : 所谓的原子性是指在一次操作或者多次操作中,要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断,要么所有的操作都不执行,多个操作是一个不可以分割的整体。
java.util.concurrent.atomic包(简称Atomic包),这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。
三个类:AtomicBoolean: 原子更新布尔类型;AtomicInteger: 原子更新整型; AtomicLong: 原子更新长整型
AtomicInteger常用方法:
| 方法 | 说明 |
| public AtomicInteger() | 初始化一个默认值为0的原子型Integer |
| public AtomicInteger(int initialValue) | 初始化一个指定值的原子型Integer |
| int get() | 获取值 |
| int getAndIncrement() | 以原子方式将当前值加1,注意,这里返回的是自增前的值。 |
| int incrementAndGet() | 以原子方式将当前值加1,注意,这里返回的是自增后的值。 |
| int addAndGet(int data) | 以原子方式将输入的数值与实例中的值(AtomicInteger里的value)相加,并返回结果。 |
| int getAndSet(int value) | 以原子方式设置为newValue的值,并返回旧值。 |
AtomicInteger原理:自旋锁 + CAS算法
CAS算法:
有3个操作数(内存值V, 旧的预期值A,要修改的值B)
当旧的预期值A == 内存值 此时修改成功,将V改为B
当旧的预期值A!=内存值 此时修改失败,不做任何操作
并重新获取现在的最新值(这个重新获取的动作就是自旋)
源码解析:
import java.util.concurrent.atomic.AtomicInteger;
public class MyAtomThread implements Runnable {
//private volatile int count = 0; //送冰淇淋的数量
//private Object lock = new Object();
AtomicInteger ac = new AtomicInteger(0);
@Override
public void run() {
for (int i = 0; i < 100; i++) {
//1,从共享数据中读取数据到本线程栈中.
//2,修改本线程栈中变量副本的值
//3,会把本线程栈中变量副本的值赋值给共享数据.
//synchronized (lock) {
// count++;
// ac++;
int count = ac.incrementAndGet();
System.out.println("已经送了" + count + "个冰淇淋");
// }
}
}
}
本文探讨了Java中多线程环境下变量原子性的问题,通过实例展示了由于线程副本导致的并发问题。文章介绍了Volatile关键字的作用,但指出其无法保证原子性。接着详细讲解了synchronized的同步机制,并提到了Atomic包中的原子类,如AtomicInteger,它们利用CAS(Compare and Swap)算法和自旋锁实现无锁编程,确保线程安全地更新变量。文中还列举了AtomicInteger的常见方法,并对CAS算法进行了简要说明。
1946

被折叠的 条评论
为什么被折叠?



