美文网首页Android开发经验谈
Android线程篇(七):多线程下的缓存一致性问题

Android线程篇(七):多线程下的缓存一致性问题

作者: 小五666 | 来源:发表于2018-03-25 14:33 被阅读0次

这篇文章,必须了解Java虚拟机的内存模型和CUP的内存架构,不了解的同学速度学习前两篇。

Java内存模型:
Android线程篇(五):Java内存模型
CPU内存架构:
Android线程篇(六):CPU内存架构

继续上篇文章的例子,稍作改动:

Int count=0
count=count+1

如果count=count+1在单线程里面运行,这个是没有任何问题的,但是在多线程中运行就会有问题,会有什么问题呢?

当线程执行count=count+1时会先从主内存读取count的值,然后复制一份到CPU的高速缓存中,对count进行+1操作,将count的结果写入高速缓存中,再将i的值刷新到主内存当中。

如果有俩个线程同时执行这个代码,我们期望的结果为2,到底会出现什么情况呢?我们继续分析。

开始时,俩个线程分别读取count的值到各自的CPU高速缓存当中,线程1和线程2对count进行+1操作,线程1将count的结果写入高速缓存中,再将i的值刷新到主内存当中,此时线程2高速缓存中,count的值还是0,进行加1操作之后,count的值为1,然后线程2把count的值写入内存,这个时候count的值还为1。

最终结果i的值是1,而不是2。这就是著名的缓存一致性问题。通常称这种被多个线程访问的变量为共享变量。

在多线程编程的时候,如果一个变量在多个CUP中都有缓存,就可能会出现缓存不一致性问题。

问题清楚了,我们如何来解决这个问题呢?

来先上个例子,至于为什么不用Int请看上篇文章:

文章链接:
Java线程并发小例子的思考,寻求大佬答疑解惑

    public Integer count = 0;
    public int TestVolatile(){
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    count++;
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<"+count);
        return count;
    }

Log:

03-18 03:00:16.098 5569-5569/com.example.myapplication I/System.out: <<<<<863
03-18 03:01:55.414 5569-5569/com.example.myapplication I/System.out: <<<<<1000
03-18 03:01:58.210 5569-5569/com.example.myapplication I/System.out: <<<<<976
03-18 03:02:00.426 5569-5569/com.example.myapplication I/System.out: <<<<<925

我们期望count结果等于1000,结果看log,都是小于1000的,那么我们如何能让结果等于我们期望的1000呢:
第一种:
采用synchronized:

public Integer count = 0;
    public Integer TestVolatile() {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    increase();
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<" + count);
        return count;
    }
    public synchronized void increase() {
        count++;
    }

Log:

03-25 14:20:02.426 15465-15465/? I/System.out: <<<<<1000
03-25 14:20:09.868 15465-15465/com.example.myapplication I/System.out: <<<<<1000
03-25 14:20:13.214 15465-15465/com.example.myapplication I/System.out: <<<<<1000

第二种:采用Lock:

    public Integer count = 0;
    Lock lock = new ReentrantLock();
    public Integer TestVolatile() {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    increase();
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<" + count);
        return count;
    }
    public void increase() {
        lock.lock();
        try {
            count++;
        } finally{
            lock.unlock();
        }

    }

这里就不贴Log了,有兴趣的朋友自己试。。。

第三种:采用AtomicInteger:

public AtomicInteger count = new AtomicInteger();
    public AtomicInteger TestVolatile() {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        Thread.sleep(10);
                    } catch (InterruptedException e) {
                    }

                    increase();
                    countDownLatch.countDown();
                }
            }).start();
        }
        try {
            countDownLatch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("<<<<<" + count);
        return count;
    }
    public void increase() {
        count.getAndIncrement();

    }

至于这几种方法都有什么优劣,他们的原理都有什么,后面的文章我们继续讲解。

相关文章

  • Android线程篇(七):多线程下的缓存一致性问题

    这篇文章,必须了解Java虚拟机的内存模型和CUP的内存架构,不了解的同学速度学习前两篇。 Java内存模型:An...

  • java并发编程(一)缓存一致性协议

    多线程环境下存在的问题 缓存一致性问题,指令重拍问题,可见性,有序性, 缓存一致性问题 在多处理器系统中,每个处理...

  • 02.线程安全性问题

    [TOC] 安全性问题概述 什么是安全性问题 多线程情况下的安全问题,是指数据的一致性问题,在多线程环境下,多个线...

  • Java多线程(5)

    Java多线程(5) CPU缓存一致性问题 因为缓存的出现,极大提高了CPU的吞吐能力,但同时也引入了缓存不一致的...

  • java之内存模型

    在 CPU 和主存之间增加缓存,在多线程场景下就可能存在缓存一致性问题,也就是说,在多核 CPU 中,每个核的自己...

  • 2018-07-23 Android 多线程编程

    前面我写过一篇文章简要介绍了一下Java多线程,今天来讲一下Android多线程,其实Android多线程和Jav...

  • Android中多线程的使用

    说完了《Java中的多线程详解》之后,今天来说一下Android中的多线程。 先来列举下Android下启用多线程...

  • Android消息机制(六) 总结

    参考Android线程的正确使用姿势Android性能优化典范之多线程篇 Android多线程编程的总结Andro...

  • JMM( java Memory Model)内存模型

    JMM是为了解决多线程对共享数据的读写一致性问题; Android中不会去考虑QPS,高并发的问题,这个是后端需要...

  • Android下多线程的实现

    Android下多线程相关 线程安全相关问题参考:java内存模型与线程 android下与多线程有关的主要有以下...

网友评论

    本文标题:Android线程篇(七):多线程下的缓存一致性问题

    本文链接:https://www.haomeiwen.com/subject/lnvtfftx.html