栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

线程安全之a++问题以及打印产生问题的数字

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

线程安全之a++问题以及打印产生问题的数字

线程安全之a++问题以及打印产生问题的数字
  • a++导致线程安全问题
1. a++多线程下数据丢失问题

案例:定义一个全局变量count,2个线程交替执行1万次count++,查看count最终结果。

public class MultiThreadsError implements Runnable {

    static int count = 0;

    @Override
    public void run() {
        for (int i = 0; i < 10000; i++) {
            count++;
        }
    }

    public static void main(String[] args) throws InterruptedException {
        MultiThreadsError multiThreadsError = new MultiThreadsError();
        Thread thread1 = new Thread(multiThreadsError);
        Thread thread2 = new Thread(multiThreadsError);

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(count);
    }
}

程序结果:count不等于20000。

问题分析

count++ 是一个复合操作:读取 - 执行 - 写入,并且当前数据状态以来上一个数据状态

线程一读取count = 0,然后执行到第二步,count + 1,此时,操作系统将线程时间片交到线程二手上,线程二执行第一步读取count = 0;

当线程一和线程二都执行完成。count 结果为1

拓展

打印出count重复执行那几次?

上代码:

public class MultiThreadsError implements Runnable {

    static int count = 0;
    static boolean[] arr = new boolean[100000000];

    static AtomicInteger counter = new AtomicInteger();
    static AtomicInteger countFail = new AtomicInteger();

    CyclicBarrier o1 = new CyclicBarrier(2);
    CyclicBarrier o2 = new CyclicBarrier(2);

    @Override
    public void run() {
        arr[0] = true;
        for (int i = 0; i < 100000; i++) {
            try {
                o2.reset();
                // 等待2个线程一起到达,再一起出发
                o1.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                throw new RuntimeException(e);
            }

            count++;

            try {
                // 等待2个线程一起到达,再一起出发
                o1.reset();
                o2.await();
            } catch (InterruptedException | BrokenBarrierException e) {
                throw new RuntimeException(e);
            }
            counter.incrementAndGet();
            synchronized (this) {
                // 先判断后执行,会产生线程竞争,导致数据错误。
                if (arr[count] && arr[count - 1]) {
                    System.out.println("fail number " + count);
                    countFail.incrementAndGet();
                } else {
                    arr[count] = true;
                }
            }
        }

    }

    public static void main(String[] args) throws InterruptedException {
        MultiThreadsError multiThreadsError = new MultiThreadsError();
        Thread thread1 = new Thread(multiThreadsError);
        Thread thread2 = new Thread(multiThreadsError);

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println(count);
        System.out.println("执行次数:" + counter.get());
        System.out.println("错误次数:" + countFail.get());
    }
}

结果:

代码分析:

  1. 使用arr数组来标记count的状态,如果2个线程产生竞争,那么假设2个线程得到的值都是1,那么执行if(arr[count])的时候就可以打印出产生竞争的那个数字。
  2. 定义了2个同步器o1和o2,环绕在count++执行前后,消除count++产生竞争对后续的代码的影响。假设如果不用同步器,那么if判断为true,但是打印的数字不一定是产生竞争的数字。
  3. 使用synchronized将判断语句锁起来,消除先判断后执行产生的线程问题。
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/835918.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号