多线程CAS、synchronized锁原理 、JUC以及死锁

本文深入探讨了Java并发编程的关键概念,包括CAS操作的介绍、实现、应用及其引发的ABA问题,synchronized锁的无锁、偏向锁、轻量级锁和重量级锁原理,Callable接口的使用以及JUC中的对象锁和Lock接口。此外,还详细讲解了死锁的产生、示例和避免方法,帮助开发者理解并发编程中的关键挑战和解决方案。
摘要由CSDN通过智能技术生成

目录

一、CAS

1、CAS的简单介绍

 2、CAS的实现

3、CAS的应用

(1)CAS实现原子类

(2)实现自旋锁

4、CAS引发的ABA问题 

(1)ABA问题的解释

​ (2)ABA问题引发的bug

 (3)ABA问题的解决方法

二、synchronized锁原理 

(1)无锁

(2)偏向锁 

​(3)轻量级锁

  (4)重量级锁

三、Callable接口 

1、java中创建线程的四种方式

2、Callable接口

 (1)创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本

(2) 创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本

​ 四、JUC的常见类

1、对象锁 juc.lock

 2、Lock接口的常用方法

 3、ReentrantLock 和 synchronized 的区别

五、死锁 

1、死锁的解释

2、死锁的形成示例

3、避免死锁 

​ 六、相关代码


一、CAS

1、CAS的简单介绍

全称Compare and swap,字面意思:”比较并交换“,一个 CAS 涉及到以下操作:

我们假设内存中的原数据V,旧的预期值A,需要修改的新值B。

     ●  比较 A 与 V 是否相等。(比较)

     ●  如果比较相等,将 B 写入 V。(交换)若不相等,说明当前线程的值A已经过时(主内存发生了变化),就将主内存的最新值V保存到工作内存中,此时无法将B写回主内存

     ●  返回操作是否成功。

(V是当前主内存值,A是当前工作内存值,B是当前线程想要修改的值)

 2、CAS的实现

针对不同的操作系统,JVM 用到了不同的 CAS 实现原理,简单来讲:

     java 的 CAS 利用的的是 unsafe 这个类提供的 CAS 操作;

     unsafe 的 CAS 依赖了的是 jvm 针对不同的操作系统实现的 Atomic::cmpxchg;                     Atomic::cmpxchg 的实现使用了汇编的 CAS 操作,并使用 cpu 硬件提供的 lock 机制保证其原子 性。

简而言之,是因为硬件予以了支持,软件层面才能做到。

3、CAS的应用

(1)CAS实现原子类

标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于这种方式来实现的.

public class AtomicTest {
    class Counter{
        //基于整型的原子类
        AtomicInteger count=new AtomicInteger();
    }

    public static void main(String[] args) {
        AtomicInteger count=new AtomicInteger();
        //等同于 ++i
        System.out.println(count.incrementAndGet());
        //等同于 i++
        System.out.println(count.getAndIncrement());
        //等同于 --i
        System.out.println(count.decrementAndGet());
        //等同于 i--
        System.out.println(count.getAndDecrement());
    }

 可以看出来,下面的代码完全没有用到锁,但是依旧是线程安全的

public class Atomic2 {
    static class Counter {
        AtomicInteger count = new AtomicInteger();

        void increase() {
            //++i   使用CAS机制保证变量的原子性
            count.incrementAndGet();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                counter.increase();
            }
        });
        t1.start();;
        t2.start();
        t1.join();
        t2.join();
        System.out.println(counter.count.get());
    }
}

工作流程:

      线程1执行cas操作,要将最新值1写回主内存,此时V==0、A==0,B==1,由于cas(V==A),所以就将1写回主内存,线程2执行cas操作,将最新值1写回主内存,此时V==1,A==0,B==1,cas(C!=A),说明主内存在线程2读取时已经有过修改,本次写回操作失败,将主内存最新值1加载到当前工作内存中,再次尝试以上操作,此时V==1,B==1,B==2,cas(V==A),就可以将最新值2写回到主内存

(2)实现自旋锁

自旋锁就是获取锁失败的线程不进入阻塞态,而是在CPU上空转(线程不让出CPU,而是跑一些无用的指令),不断查询当前锁的状态。

 this.ower:表示当前获取锁的线程

 null:期望获取锁的线程为null,表示当前自旋锁没有被任何线程持有

只有当this.ower==null时,就尝试将当前锁this.ower==Thread.cuurrentThread(),将持有锁的线程设置为当前线程

4、CAS引发的ABA问题 

(1)ABA问题的解释

ABA 的问题:

       假设存在两个线程 t1 和 t2. 有一个共享变量 num, 初始值为 A.

       接下来, 线程 t1 想使用 CAS 把 num 值改成 Z, 那么就需要

               先读取 num 的值, 记录到 oldNum 变量中.

               使用 CAS 判定当前 num 的值是否为 A, 如果为 A, 就修改成 Z.

      但是, 在 t1 执行这两个操作之间, t2 线程可能把 num 的值从 A 改成了 B, 又从 B 改成了 A

 (2)ABA问题引发的bug

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值