前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >Java多线程八股(二),CAS详解,ReentrantLock和Synchronized的区别

Java多线程八股(二),CAS详解,ReentrantLock和Synchronized的区别

作者头像
用户11305962
发布2024-11-21 16:29:34
发布2024-11-21 16:29:34
7900
代码可运行
举报
文章被收录于专栏:学习学习
运行总次数:0
代码可运行

一.CAS详解:

1.什么是CAS: 

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

CAS本质上是操作系统的一个CPU指令,操作系统把这个指令进行封装,提供了一些API,可以被C++调用,JVM又基于C++的实现,JVM也可以使用; 但是CAS指令我们一般不直接使用,而是使用JVM和标准库封装好的

2.CAS 伪代码:

下⾯写的代码不是原子的, 真实的 CAS 是⼀个原子的硬件指令完成的. 这个伪代码只是辅助理解 CAS的工作流程.

(这整个逻辑就相当于一条CPU指令, 值科学时才进行赋值,从而实现原子性的效果 )

代码语言:javascript
代码运行次数:0
复制
boolean CAS(address, expectValue, swapValue) {
 if (&address == expectedValue) {
 &address = swapValue;
 return true;
 }
 return false;
}

3.CAS 的应用:

应用一:实现原自类

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

典型的就是 AtomicInteger 类. 其中的 getAndIncrement 相当于 i++ 操作

使用代码: 

代码语言:javascript
代码运行次数:0
复制
public class Demo {
    private static AtomicInteger count = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {

                //`getAndIncrement()` 会原子性地将当前值增加 1
                count.getAndIncrement();
            }
        });

        Thread t2 = new Thread(()->{
            for (int i = 0; i < 50000; i++) {

                //`getAndIncrement()` 会原子性地将当前值增加 1
                count.getAndIncrement();
            }
        });

        t1.start();
        t2.start();

        t1.join();
        t2.join();

        //`get()` 获取当前值。
        System.out.println(count.get());

    }
}

AtomicInteger 类CAS伪代码理解: 

理解:value相当于内存,oldValue相当于寄存器

value==oldValue值就科学,直接返回oldValue,否则重新从内存拿值到寄存器重新判断值是否科学

代码语言:javascript
代码运行次数:0
复制
class AtomicInteger {
  private int value;//value理解为内存
  public int getAndIncrement() {
  int oldValue = value;
  while ( CAS(value, oldValue, oldValue+1) != true) {
   oldValue = value;//value==oldValue值就科学,直接返回oldValue,否则重新从内存拿值到寄存器重新判断值是否科学
  }
 return oldValue;
 }
}

应用二: 实现自旋锁 

代码语言:javascript
代码运行次数:0
复制
public class SpinLock {
 private Thread owner = null;
 public void lock(){
 // 通过 CAS 看当前锁是否被某个线程持有. 
 // 如果这个锁已经被别的线程持有, 那么就⾃旋等待. 
 // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. 
 while(!CAS(this.owner, null, Thread.currentThread())){
 }
 }
 public void unlock (){
 this.owner = null;
 }
}

理解:owner == null,锁就没有被别的线程占有,直接返回

如果owner != null,该锁就被的线程占有,这时返回false,!取反置为true,继续往循环里走,但是这个循环没有东西,会进行“忙等”快速拿到别的线程抛弃的锁,这就是自旋锁 


3.CAS 的 ABA 问题: 1.什么是ABA 问题,某个线程把内存的值修改为A,另一个线程把内存的值修改为B,又修改成原来的A (站在值科学的角度,最后为A,说明没有被其他线程修改过,实则不然只是被多修改了而已)  这样在极端情况可能会导致线程安全问题 (说起极端情况:闰秒问题也是服务器开发中的一个极端问题,就是某个时间点时间往前跳了导致代码逻辑上出现错误)  在取钱的时候,多个线程反复进行存钱(+)取钱(-)就可能出现转账余额问题(略)   


2.解决: 这种ABA问题原因在于,有其他线程参与进来,进行加也进行减少余额, 想要避免这种ABA问题就要使用版本号,这个版本只进行加,另一个版本只进行减(每次修改一次,版本号就加1) 



二.Callable接口,ReentrantLock和Synchronized的区别:

1.Callable接口和Runnable接口一样都可以超创建线程: 

但是 Callable接口需要重写call方法,并且这个方法还有泛型的返回值


需要真正执行任务还不足以,因为Callable只定义了一个带有返回类型的任务,并没有真正执行 ,还需要搭配futureTask和Thread对象使用  


代码: 

代码语言:javascript
代码运行次数:0
复制
public static void main(String[] args) throws ExecutionException, InterruptedException {
        /** Callable只定义了一个带有返回类型的任务,并没有真正执行
         * 需要搭配futureTask和Thread对象使用
         *
         */
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int result = 0;
                for (int i = 0; i < 100; i++) {
                    result += i;
                }
                return result;
            }
        };

        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread t1 = new Thread(futureTask);
        t1.start();

        // get 操作就是获取到 FutureTask 的返回值. 这个返回值就来自于 Callable 的 call 方法.
        // get 可能会阻塞. 如果当前 线程 执行完毕, get 拿到返回结果.
        // 如果当前线程还没执行完毕, get 会一直阻塞.
        System.out.println(futureTask.get());

    }

2.ReentrantLock和Synchronized的区别:  (1).Synchronized是关键字,ReentrantLock是JVM内部通过C++实现的标准库的类 (2).Synchronized是通过代码块控制加锁解锁,ReentrantLock控制加锁解锁是通过Lock和unLock方法控制的 (要注意unLock方法没有被执行到)  (3).ReentrantLock除了还提供了 tryLock方法(加锁成功返回ture,加锁失败返回false,也有带次数的版本) 

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档