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

目录:

一.CAS详解:

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

一.CAS详解:

1.什么是CAS:
CAS: 全称Compare and swap,字⾯意思:"比较并交换",⼀个 CAS 涉及到以下操作。
CAS本质上是操作系统的一个CPU指令,操作系统把这个指令进行封装,提供了一些API,可以被C++调用,JVM又基于 C++的实现,JVM也可以使用; 但是CAS指令我们一般不直接使用,而是使用JVM和标准库封装好的


2.CAS 伪代码:
下⾯写的代码不是原子的, 真实的 CAS 是⼀个原子的硬件指令完成的. 这个伪代码只是辅助理解 CAS的工作流程.
(这整个逻辑就相当于一条CPU指令, 值科学时才进行赋值,从而实现原子性的效果 )

java 复制代码
boolean CAS(address, expectValue, swapValue) {
 if (&address == expectedValue) {
 &address = swapValue;
 return true;
 }
 return false;
}

3.CAS 的应用:

应用一:实现原自类
标准库中提供了 java.util.concurrent.atomic 包, 里面的类都是基于这种方式来实现的.
典型的就是 AtomicInteger 类. 其中的 getAndIncrement 相当于 i++ 操作

使用代码:

java 复制代码
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,否则重新从内存拿值到寄存器重新判断值是否科学

java 复制代码
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;
 }
}

应用二: 实现自旋锁

java 复制代码
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对象使用


代码:

java 复制代码
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,也有带次数的版本)

相关推荐
疯一样的码农6 分钟前
Apache Maven简介
java·maven·apache
聊无生10 分钟前
JavaSrcipt 函数高级
开发语言·前端·javascript
小安同学iter18 分钟前
Java进阶五 -IO流
java·开发语言·intellij-idea
Hello World and You19 分钟前
R ggplot2 绘图细节 geom_text展示不全 y轴坐标细节 x轴标题
开发语言·r语言
sukalot23 分钟前
windows C#-异步文件访问
开发语言·c#
尽兴-29 分钟前
Redis模拟延时队列 实现日程提醒
java·redis·java-rocketmq·mq
熬夜学编程的小王41 分钟前
【C++篇】从基础到进阶:全面掌握C++ List容器的使用
开发语言·c++·list·双向链表·迭代器失效
悄悄敲敲敲43 分钟前
C++:智能指针
开发语言·c++
书埋不住我1 小时前
java第三章
java·开发语言·servlet
boy快快长大1 小时前
将大模型生成数据存入Excel,并用增量的方式存入Excel
java·数据库·excel