【多线程之CAS、Synchronized优化、JUC】

CAS

概念

CAS:compare and swap 比较和交换。有一个寄存器A、寄存器B和一个内存M,寄存器A和内存M的值进行比较,如果不相同则无事发生,如果相同则把寄存器A的值和M进行交换(此时,我们不关心寄存器B中的值,只关心内存M的值,相当于赋值操作)

应用场景

最常用的两个场景:

  1. 实现原子类

    伪代码实现原子类中count++操作

class AtomicInteger{
    private int value;
    public int getAndIncrement(){
        int oldvalue = value;//LOAD
        while(CAS(value,oldvalue,oldvalue+1) != true){
            oldvalue = value;
        }
        return oldvalue;
    }
}

这其实是标准库中已经封装好了的一个类。

package threading;

import java.util.concurrent.atomic.AtomicInteger;

// 原子类(底层基于CAS实现)java已经封装好了可以直接使用
public class Demo31 {
    public static void main(String[] args) throws InterruptedException {
//        //相当于++count
//        count.incrementAndGet();
//        //相当于count--
//        count.getAndDecrement();
//        //相当于--count
//        count.decrementAndGet();
        AtomicInteger count = new AtomicInteger(0);
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                //相当于count++
                count.getAndIncrement();
            }
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 5000; i++) {
                //相当于count++
                count.getAndIncrement();
            }
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println(count.get());
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SLOZA0nv-1667129030670)(D:\图片\MySql+javaEE\CAS实现原子类.png)]

  1. 实现自旋锁

    跟实现原子类差不多

ABA问题

在CAS中,进行比较的时侯,寄存器A和内存M的值相同,但是你无法判定M始终没变,还是变了又变回来了。

只要有一个记录,能够记录上内存中数据的变化,就能解决ABA问题了。

如何进行记录呢?

另外搞一个内存保存M的"修改次数"[版本号]{只增不减}或者是上次修改的时间[只增不减],通过这个方法都能解决ABA问题。此时当我们修改操作时,寄存器A就不只是读取内存M的值了,进行比较的时候也不是比较寄存器A和内存M的值,而比较两个的版本号/上次修改时间。

synchronized的优化

synchronized加锁的具体过程

锁升级

synchronized为了减轻使用者的负担,内部就实现了自适应这样的操作。

  1. 偏向锁: 设置一个状态,只在必要的时候加锁,能不加就不加
  2. 轻量级锁:如果当前场景中,锁竞争不激烈,则以轻量级锁状态工作。
  3. 重量级锁:如果当前场景中,锁竞争不激烈,则以重量级锁状态工作。

锁消除

编译器优化操作,当不需要加锁时但是你的代码中加上了synchronized,jvm就自动把锁干掉,不过这个编译器优化操作是在它有十足把握时才会这样做。

锁粗化

锁的粒度就是synchronized的对应代码块包含多少代码,包含的代码少,粒度就细,包含的代码多,粒度就粗。锁粗化就是把细粒度的锁->粗粒度的锁。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qtsXRGpw-1667129030671)(D:\图片\MySql+javaEE\锁粗化.png)]

JUC

java.util.concurrent (并发)这个包中都是和多线程有关的

Callable

概念

类似于Runnable任务,但是是具有返回值的任务,Runnable是没有返回值的。不过Callable任务不能直接传给线程,要通过中间类FutureTask,FutureTask存在的意义就是为了让我们能够获取结果(相当于小票)。

代码实例

public class Demo9 {
    //创建一个线程求 1+2+3+···+1000的值
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        //使用Callable创建任务
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Object call() throws Exception {
                int count = 0;
                for (int i = 1; i <= 1000; i++) {
                    count += i;
                }
            return count;
            }
        };
        FutureTask<Integer> future = new FutureTask<>(callable);
        Thread t = new Thread(future);
        t.start();
        //通过future获取结果
        System.out.println(future.get());
    }
}

ReentrantLock

概念

是一种可重入锁,synchronized也是可重入锁,但是synchronized也有一些功能做不到,而ReentrrantLock就是对synchronized的补充。

有三种核心方法:

  1. lock()
  2. unlock()
  3. trylock()

缺点:由于1,2需要代码中显式的加锁解锁,可能会出现加锁了却没解锁的情况,比如lock和unlock中间return了,所以一般他们需要搭配try,finally来使用。

优点:

  1. trylock可以试着看能不能加上锁
  2. 可以搭配condition类实现的,可以指定唤醒莫个线程

代码实例

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class Demo30 {
    public static void main(String[] args) {
        ReentrantLock reentrantLock = new ReentrantLock(true);
        try {
            //加锁
            reentrantLock.lock();
        }finally {
            //如果两者之间有return   unlock可能执行不到  所以一般配合try finally使用
            //解锁
            reentrantLock.unlock();
        }

    }
}

CountDownLatch

概念

同时等待 N 个任务执行结束.

代码实例

  • 构造 CountDownLatch 实例, 初始化 4 表示有 4 个任务需要完成.
  • 每个任务执行完毕, 都调用 latch.countDown() . 在 CountDownLatch 内部的计数器同时自减.
  • 主线程中使用 latch.await(); 阻塞等待所有任务执行完毕. 相当于计数器为 0 了.
import java.util.concurrent.CountDownLatch;

public class Demo32 {
    //CountDownLatch 模拟跑步比赛
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch count = new CountDownLatch(4);
        for (int i = 0; i < 4; i++) {
            Thread t = new Thread(() -> {
                System.out.println(Thread.currentThread().getName() + " 起跑");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + " 撞线");
                count.countDown();
            });
            t.start();
        }
        count.await();
        System.out.println("比赛结束");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值