线程安全之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);
    }
}

image-20220424110515105

程序结果: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());
    }
}

结果:

image-20220424170044364

代码分析:

  1. 使用arr数组来标记count的状态,如果2个线程产生竞争,那么假设2个线程得到的值都是1,那么执行if(arr[count])的时候就可以打印出产生竞争的那个数字。
  2. 定义了2个同步器o1和o2,环绕在count++执行前后,消除count++产生竞争对后续的代码的影响。假设如果不用同步器,那么if判断为true,但是打印的数字不一定是产生竞争的数字。
  3. 使用synchronized将判断语句锁起来,消除先判断后执行产生的线程问题。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值