20240428-并发编程特性-原子性
1.原子性
1.1 JMM概述
不同的硬件和不同的操作系统在内存操作上有一定差异,java为了解决相同代码在不同操作系统上出现的各种问题,用JMM(java memory model)java内存模型,
屏蔽了各种硬件和操作系统带来的差异,让java可以跨平台
1.2 JMM特性
JMM规定,所有变量都会存储在主内存中,在操作的时候,需要从主内存复制一份到线程内存,在线程内部做计算,然后写回主内存中(不一定会及时操作)
1.3 原子性的定义
- 原子性是指一个操作是不可分割且不可中断的,一个线程在执行时,不受另外一个线程的影响
// 这个操作是不可分割的,是原子操作
int i = 0;
// 这个操作不是原子操作,要现查询i的值,再把i赋值给j
int j = i;
// 这个操作也不是原子操作,要先查询i的值,然后给i的值加1,然后再赋值给i
i++;
- 测试代码
package com.ysf;
public class Tst01Atomic {
private static int count = 0;
public static void increment(){
count ++;
try {
Thread.sleep(10L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(()->{
for (int i = 0;i<100;i++){
increment();
}
},"plus-1");
Thread t2 = new Thread(()->{
for (int i = 0;i<100;i++){
increment();
}
},"plus-2");
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
}
}
- 从运行结果可以看出,与预期的200不符,因为++操作不是原子操作
- 如何保证原子性呢?
- 使用单线程
- 不操作临界资源
- 使用synchronized锁
- 使用CAS锁
- 使用lock锁
- 使用ThreadLocal
1.4 对于++操作,看下CPU到底干了啥
cd \study-juc\day-0428\src\main\java\com\ysf
javac Tst01Atomic
javap -v Tst01Atomic.class
public static void increment();
descriptor: ()V
flags: ACC_PUBLIC, ACC_STATIC
Code:
stack=2, locals=1, args_size=0
0: getstatic #2 //从主内存取值
3: iconst_1
4: iadd //++
5: putstatic #2 //往主内存放值
8: ldc2_w #3
11: invokestatic #5
14: goto 22
- 多线程情况下,该代码可能存在的过程
- 线程1、线程2同时取值为1
- 线程1、线程2同时计算为2
- 线程1、线程2同时放值