原子性
所有操作作为一个不可分割的整体,操作要么同时成功,要么同时失败。
原子性问题引发的多线程问题:
现有如下问题我要让100个线程,每个线程送100朵花给小哈,但是最后发现不足10000朵花。
原因分析如下:
count++有如下操作:
1.线程将共享变量拷贝到线程栈副本
2.将副本的值++
3.将副本的值赋值给堆中的共享变量
如果在线程A在第二步还没到第三步,此时线程B进行第一步,则线程A、B执行count++以后,count在同一个值位置自增了两次,也就是少送了一朵花。
案例代码如下:
public class HuaThread implements Runnable { private int count = 0; @Override public void run() { for(int i = 0 ;i < 100; i ++){ count++; System.out.println("给小哈送花第"+count+"朵"); } } } public class Test { public static void main(String[] args) { HuaThread huaThread = new HuaThread(); for(int i=0;i<100;i++){ new Thread(huaThread).start(); } } }
解决方案1:AtomicInteger
原子整形类实现count++的整体一致性。
public class HuaThread implements Runnable { private AtomicInteger count = new AtomicInteger(0); @Override public void run() { for(int i = 0 ;i < 100; i ++){ System.out.println("给小哈送花第"+count.incrementAndGet()+"朵"); } } } public class Test { public static void main(String[] args) { HuaThread huaThread = new HuaThread(); for(int i=0;i<100;i++){ new Thread(huaThread).start(); } } }
解决方案2:synchronized
用synchronized同步代码块锁住访问共享资源的代码,每次只允许一个线程对共享数据操作。
public class HuaThread implements Runnable { private int count = 0; @Override public void run() { synchronized (this) { for(int i = 0 ;i < 100; i ++){ count++; System.out.println("给小哈送花第"+count+"朵"); } } } } public class Test { public static void main(String[] args) { HuaThread huaThread = new HuaThread(); for(int i=0;i<100;i++){ new Thread(huaThread).start(); } } }
AtomicInteger的原理
自旋+CAS算法
CAS算法中有三个操作数
1.内存值:也就是被修改的共享数据
2.旧值:从内存中读取的共享数据
3.要修改的值:旧值进行操作后的值
算法流程如下:
若旧值与内存中的值一致,则说明没有其他线程操作过共享数据,此时将内存中的值修改为要修改的值,修改成功;
若旧值与内存中的值不一致,说明其他线程操作过了内存中的值,此时修改失败,从内存中重新获取值,继续操作;(从内存中重新获取值的流程叫做自旋)