JMM之原子性

不可分割,完整性。也就是说某个线程正在做某个具体业务时,两头不能够被加塞或者被宰割,须要具体实现,要么同时胜利,要么同时失败。

代码测试volatile是否保障原子性

咱们创立了20个线程,而后每个线程别离循环1000次,来调用number++的办法

class MyData {    // 定义int变量    volatile int number = 0;    public void addPlusPlus() {        number++;    }}
public class Test {    public static void main(String[] args) {        MyData myData = new MyData();        // 创立20个线程,线程外面进行1000次循环        for (int i = 0; i < 20; i++) {            new Thread(() -> {                for (int j = 0; j < 1000; j++) {                    myData.addPlusPlus();                }            }).start();        }    /*         须要期待下面20个线程都执行结束后,再用main线程获得最终的后果        这里判断线程数是否大于2,为什么是2?因为默认有两个线程的,一个main线程,一个gc线程    */        while (Thread.activeCount() > 2) {            Thread.yield(); // yield示意不执行        }        System.out.println("线程运行完后,number的值为:" + myData.number);    }}

线程执行结束后打印number的值,假如volatile保障原子性的话,那么最初输入的值应该是20 * 1000 = 20000。

最终后果咱们会发现,number输入的值并没有20000,而且是每次运行的后果都不统一的,这阐明了volatile润饰的变量不保障原子性。

为什么呈现数据失落

A线程和B线程同时批改各自工作空间里的内容。因为可见性,须要从新写入内存,然而A线程在写入的时候,BB线程也同时写入,导致A线程的写入操作被挂起,这样造成B线程的写入后,A线程笼罩了B线程的值,造成了数据失落的问题。

咱们将一个简略的n++操作,针对 add() 这个办法的字节码文件进行剖析:

volatile int n = 0;public void add() {    n++;}
  public void add();    Code:       0: aload_0           1: dup       2: getfield             5: iconst_1       6: iadd       7: putfield            10: return

咱们能发现n++这条命令,被拆分为3个指令

  1. 执行getfield 从主内存拿到原始n
  2. 执行iadd 进行加1操作
  3. 执行putfileld 把累加后的值写回主内存

如果咱们没有加synchronized那么第一步就可能存在着,三个线程同时通过getfield命令,拿到内存中的n值。而后三个线程,各自在本人的工作内存中进行加1操作,然而他们并发执行iadd命令的时候,因为只能一个进行写,所以其余操作会被挂起。假如A线程,先进行了写操作,在写完后,volatile的可见性应该通知其余两个线程,主内存的值被批改了。然而因为太快,其余两个线程,陆续执行iadd命令,这就造成了其余线程没有接管到主内存n的扭转,从而笼罩了原来的值,呈现写失落,这样也就让最终的后果少于20000。

如何解决

因而阐明,在多线程环境下n++是非线程平安的,如何解决呢?

  • 在办法上加上synchronized

    public synchronized void addPlusPlus() {    number ++;}

引入synchronized关键字后,保障了该办法每次只可能一个线程进行拜访和操作,最终输入的后果也就为20000。

  • 为了解决n++,引入重量级的同步机制,有种杀鸡焉用牛刀的感觉。

    咱们还能够应用JUC上面的原子包装类,即int类型的number,能够应用AtomicInteger来代替

    //创立一个原子Integer包装类,默认为0AtomicInteger number = new AtomicInteger();public void addAtomic(){    number.getAndIncrement();    //相当于number++}