乐观锁与悲观锁

5 篇文章 0 订阅

什么是锁,为什么要用锁?

别着急,我们先看看下面的代码的执行结果:
这段代码的要求是输出0-999的数

package demo.test;
public class Test1 {
    static Integer num=0;
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    while (num < 100) {
                        System.out.println("ThreadName:" + Thread.currentThread().getName() + "\tnum:" + num++);
                    }
                }
            });
            thread.start();
        }
    }
}

RESULT:
在这里插入图片描述
可以看到两个线程都输出了0,这就代表他们竞争资源的时候同时拿到了资源进行操作,这表示线程是不安全的,那么如何准确无误的输出想要的结果呐?

我们就需要用到synchronized关键字,作用是同步线程
代码如下

package demo.test;
public class Test1 {
    static Integer num=0;
    public static void main(String[] args) {
        for (int i = 0; i < 2; i++) {
            final Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (Test1.class){
                        while (num < 100) {
                            System.out.println("ThreadName:" + Thread.currentThread().getName() + "\tnum:" + num++);
                        }
                    }
                }
            });
            thread.start();
        }
    }
}

那么sychronized是如何实现同步的呐?
这里先介绍一样新事物叫Monitor,它是基于操作系统的mutex lock实现的,你可以把想资源想象成一个房间,想要使用这个资源的线程为等待的客人,这个房间每次就只能进入一个客人,一个客人进入,那么其他客人只能等待,Monitor的翻译为监控器,你可以把它认为为一个房间,它严格的把控着这个房间只允许被一个人使用。

下图是sychronized的实现机制,客人们排队在Entry set等待,如果一个客人A进入了房间,但是需要他暂时腾出房间给客人B使用,那么他将进入wait set等待,等客人B用完exit后,客人B通过notify重新唤醒客人A继续完成任务.

在这里插入图片描述
但是由于java线程是映射的操作系统系统线程,每次挂起和唤醒都需要操作系统切换内核态,会影响的程序的性能,甚至有时候切换线程比任务本身的时间还长,所以这是一种重量级的锁机制,被称为重量级锁,是悲观锁,在java6以上引入进行了优化,引入了轻量级锁和偏向锁.

那有没有一种机制能够既能无锁又能实现同步呐?cas算法
以下是代码实现:

package demo.test;

import java.util.concurrent.atomic.AtomicInteger;

public class Test1 {
    static AtomicInteger atomicInteger = new AtomicInteger(-1);
    public static void main(String[] args) {

        for (int i = 0; i < 2; i++) {
            final Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    synchronized (Test1.class){
                        while (atomicInteger.get()+1 < 100) {
                            System.out.println("ThreadName:" + Thread.currentThread().getName() + "\tnum:" + atomicInteger.incrementAndGet());
                        }
                    }
                }
            });
            thread.start();
        }
    }
}

cas是如何做到无锁同步的呐?
cas 其实是(compare and swap)比较和交换的缩写,举个例子,比如有两个人,他们分别是张三和李四,他们同时看到了一个美女都想和她约会,假设美女有一个牌子,当美女处于空闲状态为0,处于约会状态为1,那么张三和李四只能在美女处于状态0和她约会,现在美女刚好空闲牌子为0,张三和李四同时起跑,张三快李四一步,率先把牌子翻为1,李四看到就只能回去等待,等美女空闲了再去。

看完这个例子就很好理解cas了,假设有两个线程a,b同时受到资源的状态值变为0,那么他们会产生两个值,一个为old value 代表之前读到资源的值,new value为1
在这里插入图片描述
现在假设a线程率先到达,那么它会比较资源状态值和它的old value是否相同,如果相同就会把资源状态值改为它的new Value,当b到达时发现资源的状态值已经为1,和自己的old value不相等,于是自旋等待,自旋等待就是过一段时间B再去和其他线程竞争,直到成功或者超时放弃,关于自旋等待也有一种优化方式叫做适应性自旋,感兴趣的可以自己了解一下
在这里插入图片描述
在这里插入图片描述
那现在存在还一个一个问题,就是比如之前张三和李四如果同时到达把牌子翻为1怎么班,3个人一起约会岂不是很尴尬,所以对比并且翻牌的这个动作在同时只能由一个人完成,cas在对比和修改资源状态值同时也只能被一个线程完成,那cas是如何避免发生这种情况实现同步的呐,实现cas的原子性呐,难道还需要用锁嘛?不是,现在
不同的架构都提供指令级别的cas原子操作,比如在x86下,cmpxchg支持cas,arm的LL/SC来实现cas,cpu已经原生的支持cas,上层调用即可,上面代码即是java的调用来实现cas

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值