JAVA多线程基础篇 5、原子性与锁

1. 竞态条件

在并发编程中,多线程会抢占运行各自的代码片段,当访问共享数据时会产生不正确的结果。

public class ConditionRaceExp {

    private static volatile int counter = 0;

    public static void main(String[] args) throws InterruptedException {

        Thread[] threads = new Thread[1000];

        for(int i=0;i<1000;i++){
            threads[i] = new Thread(()->{
                for(int k=0;k<10000;k++)
                    counter++;
            });
        }

        for(int i=0;i<1000;i++){
            threads[i].start();
        }

        for(int i=0;i<1000;i++){
            threads[i].join();
        }
        System.out.println(counter);
    }
}

预期结果应该是1000000,但实际运行结果与预期不符。

conditionrace

原因是因为在执行counter++时,等同于counter = counter+1,在多线程环境中这句命令会被打断,比如A线程在执行该语句时,counter瞬时的值为100,A线程执行counter+1,得到值为101,在赋值回counter时,问题发生了——B、C、D线程抢占了CPU资源,并将counter值加到了103,等A线程回到CPU运行的时候,又会把101赋值给counter。 此种情况被成为竞态条件。

2. 原子性

要让程序能得到预期的结果,需要让涉及到竞态条件的程序指令的原子性不被破坏。此时需要将程序进行保护。JAVA提供了锁机制来保护程序,根策略不同可以分为悲观锁乐观锁两种。

2.1 悲观锁

首先需要定义一个对象,锁是上在某个普通对象上的,也可以是某个class对象上。上锁的过程其实就是在对象头上更改状态位、以及写入自己的信息。如果没有指明,则根据方法来判定,如果是实例方法,则锁在this对象上。如果是静态方法,则锁在这个实例所在的类对象上。

Java调用锁有多种方法,一般使用synchronized方法对象。

tips:在发生异常后,线程会自己释放锁的。

Object o = new Object();
synchronized(o){
    //此时,这把锁上在 o 这个对象上
}

synchronized(T.class){
    //此时,是上锁在T.class 这个对象,这个对象是Class类型的,是类加载的虚拟机后创建的。
}

2.2 乐观锁

乐观锁的实现是CAS(Compare and Swap)算法,CAS算法并没有真正“锁定”内存的对象,也被称为无锁算法。
CAS底层原理是,CAS在为对象赋值的时候,会带上对象的预期的值,将预期值和当前值进行比较(Compare),如果是一致的,就进行赋值(Swap)。否则继续重试,因此也称为自旋锁。CAS存在ABA的问题,一般是通过版本号或时间戳加上数据进行双重检验。
CAS在底层是由操作系统保证的,操作系统会调用指令 lock cmpxchg 对缓存行或总线加锁,保证原子性。

2.3 两种锁的比较

尽管悲观锁需要对锁资源进行抢占和排队,但是排队时线程并不占据CPU资源;而乐观锁的线程都会持续自旋(循环轮询)占用CPU的时间片段,因此根据线程并发数和实际测试情况,才能判断那种锁的效率高。

3.加锁的方法

  • 使用synchronized关键字
  • 使用ReentrantLock对象
  • 使用JUC库的各种工具类(详见进阶篇)

总结

锁能保证原子性。但是并不代表上锁就一定安全,程序的设计需要考虑周到。锁分为乐观锁和悲观锁,悲观锁基于对象头锁定,乐观锁基于CAS算法。对于并发数少、执行时间比较短的情况,建议使用CAS;否则建议使用悲观锁。

多线程系列在github上有一个开源项目,主要是本系列博客的实验代码。

https://github.com/forestnlp/concurrentlab

如果您对软件开发、机器学习、深度学习有兴趣请关注本博客,将持续推出Java、软件架构、深度学习相关专栏。

您的支持是对我最大的鼓励。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

悟空学编程

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值