学习多线程CAS及相关知识

devtools/2024/10/18 2:32:01/

多线程

  • CAS实现自旋锁
  • CAS的ABA问题
  • Callable接口
  • ReentrantLock
  • 信号量Semaphone
  • CountDownLatch组件
  • 小结

java">书接上回, 上篇博客中总结了synchronized的原理和CAS的实现原子类, 我们将要继续学习CAS实现自旋锁, CAS中的ABA问题, Callable创建线程等等..

CAS实现自旋锁

首先我们来看一段伪代码:

java">public class SpinLock {// owner表示是哪个线程持有这把锁, 设为null表示当前线程没有加锁private Thread owner = null;public void lock(){// 通过 CAS 看当前锁是否被某个线程持有. // 如果这个锁已经被别的线程持有, 那么就自旋等待. // 如果这个锁没有被别的线程持有, 那么就把 owner 设为当前尝试加锁的线程. // Thread.currentThread(): 获取当前线程的引用// 如果该先线程处于加锁状态, 就会返回false, 就会进入循环等待...while(!CAS(this.owner, null, Thread.currentThread())){	}}public void unlock (){this.owner = null;}
}

CAS的ABA问题

通过比较寄存器和内存的值,通过这里的是否相等,来判定内存的值是否发生了改变.
如果内存的值变了, 就存在其它线程修改.
如果内存的值没变, 就没有别的线程修改, 后面进行修改就是安全的.

那么问题来了: 如果内存的值没变, 就一定没有别的线程修改吗?
这就是ABA问题… A->B->A
在这里插入图片描述
即使以上情况发生概率很小, 但它还是会发生的, 就需要我们去处理!

javascript">这个时候我们就引入了版本号, 通过版本号的值是否被修改, 来判断数据有没有修改.

Callable接口

Callable是一个interface, 相当于把线程封装成"返回值".
我们可以通过Callable创建线程:

java">public static void main(String[] args) throws ExecutionException, InterruptedException {Callable<Integer> callable = new Callable<Integer>() {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i < 1000; i++) {sum += i;}return sum;}};FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();// 如果call方法没执行完, 会进入阻塞等待.Integer ret = futureTask.get();System.out.println(ret);}

Runnable 能表示一个任务, 通过run方法, 返回void.
Callable 也能表示一个任务, 通过call方法, 返回一个具体的泛型参数.
如果在设计多线程的地方, 我们更看重过程, 推荐使用Runable,如果更看重结果, 更推荐使用Callable.
要注意的是, Callable不能直接作为Thread的构造方法参数,要引入FutureTask.
用FutureTask对象作为Thread的构造方法参数.

ReentrantLock

ReentrantLock也是与synchronized类似的, 都是一个加锁的组件.
但它要手动设置lock():加锁和unlock(): 解锁.
ReentrantLock特点:

  1. 提供tryLock方法进行加锁:
    对于lock方法, 加锁失败就进入阻塞等待.
    对于tryLock方法, 加锁失败就返回false或者在规定的等待时间中返回, 给我们提供了更多的操作空间.
  2. ReentrantLock有两种加锁模式, 可以工作在公平锁状态下, 也可以工作在非公平锁状态下.
  3. ReentrantLock也具有等待通知功能, 搭配Condition类来使用.要比synchronized的wait,notify方法功能更强.
    实际开发中, 多线程开发, 还是首选synchronized.

信号量Semaphone

信号量, 用来表示 "可用资源的个数". 本质上就是一个计数器.
我们申请一个资源, 计数器-1, 称为p操作.
我们释放一个资源, 计数器+1, 称为v操作.

我们利用代码来熟悉pv操作过程:

java">public static void main(String[] args) throws InterruptedException {// 申请4个可用资源Semaphore semaphore = new Semaphore(4);// 申请一个资源semaphore.acquire();System.out.println("p操作");// 申请一个资源semaphore.acquire();System.out.println("p操作");// 申请一个资源semaphore.acquire();System.out.println("p操作");// 申请一个资源semaphore.acquire();System.out.println("p操作");// 释放一个资源semaphore.release();System.out.println("v操作");// 申请一个资源semaphore.acquire();System.out.println("p操作");}

CountDownLatch组件

作用: 把主线程拆成多个线程进行工作, 用来提高执行效率,比如说IDM下载器, 就可以分配多个线程来下载.

java">public static void main(String[] args) throws InterruptedException {// 分为10个线程CountDownLatch count = new CountDownLatch(10);for (int i = 0; i < 10; i++) {Thread thread = new Thread(() -> {System.out.println("线程" + i + "开始工作");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程" + i + "结束工作");// 象征任务结束count.countDown();});thread.start();}// 所有线程都已经执行完 await->allwaitcount.await();System.out.println("所有线程都执行完了");}

for循环中的i其实是访问不到的, 此处设计变量捕获, 它会给我们报final修饰或者像final的对象.

在这里插入图片描述

java">public static void main(String[] args) throws InterruptedException {// 分为10个线程CountDownLatch count = new CountDownLatch(10);for (int i = 0; i < 10; i++) {int n = i;Thread thread = new Thread(() -> {System.out.println("线程" + n + "开始工作");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}System.out.println("线程" + n + "结束工作");// 象征任务结束count.countDown();});thread.start();}// 所有线程都已经执行完 await->allwaitcount.await();System.out.println("所有线程都执行完了");}

此时只要新创建一个变量, 就可以了.我们新创建的变量的n, 实际上就是没有改的, 也就是像final的变量.

小结

多线程方面的知识已经总结完了, 我将会复习文件操作的知识, 有收获的小伙伴多多支持.


http://www.ppmy.cn/devtools/7066.html

相关文章

Kotlin 中如何使用 Fuel 库进行代理切换?

随着互联网的快速发展&#xff0c;网络编程在现代软件开发中变得越来越重要。无论是构建移动应用、Web 应用还是后端服务&#xff0c;都需要与网络进行交互。而代理服务器在网络通信中扮演着至关重要的角色&#xff0c;它可以帮助我们实现匿名访问、提高访问速度、解决网络限制…

什么是显卡服务器?

显卡服务器又叫做GPU服务器,是基于GPU的应用于视频编解码、深度学习和科学计算等多种场景的快速、稳定、弹性的计算服务&#xff0c;显卡服务器是一种用于计算机科学技术领域的计算机以及配套设备&#xff0c;有着出色的图形处理能力和高性能计算能力提供极致计算性能&#xff…

密码学 | 承诺:Pedersen 承诺 + ZKP

​ &#x1f951;原文&#xff1a;Toward Achieving Anonymous NFT Trading &#x1f951;写在前面&#xff1a;看了篇 22 年 SCI 3 区论文&#xff0c;里面提到在 Pedersen 承诺的揭示阶段可以使用零知识证明&#xff0c;而不必揭示消息明文和随机数。姑且记录一下这个方法。…

TCP/IP协议—HTTP

TCP/IP协议—HTTP HTTP协议HTTP通讯特点HTTP通讯流程 HTTP请求报文请求方法 HTTP应答报文状态码 HTTP协议 超文本传输协议&#xff08;Hypertext Transfer Protocol&#xff0c;HTTP&#xff09;是一种请求-响应的协议&#xff0c;用户可以通过HTTP向服务器上传、下载数据。HT…

安宝特方案 | AR工业解决方案系列-工厂督查

在工业4.0时代&#xff0c;增强现实&#xff08;AR&#xff09;技术正全面重塑传统工业生产&#xff0c;在工厂监督领域&#xff0c;其应用不仅大幅提升了生产效率、监测准确性和规范执行程度&#xff0c;而且为整体生产力带来了质的飞跃。 01 传统挑战与痛点 在制造业生产流程…

营业执照OCR接口在电商行业中的具体应用

在当今快速发展的电子商务时代&#xff0c;营业执照OCR接口技术的应用为电商行业带来了深远的影响。这项技术通过自动识别和提取营业执照图像中的文字信息&#xff0c;不仅极大提高了数据处理的速度和准确性&#xff0c;而且还为电商平台的风险管理和用户体验优化提供了强有力的…

说说Java 8 引入的Stream API

介绍背景 Stream API&#xff0c;这是一种高效、易于使用的数据处理方式&#xff0c;它可以极大提高程序员对集合数据操作的效率和便利性。 1. Stream的基本概念 Java Stream是一种数据流&#xff0c;它可以用于对集合、数组或者其他支持的数据源进行批量操作。它不是数据结…

基于开源IM即时通讯框架MobileIMSDK:RainbowChat v11.5版已发布

关于MobileIMSDK MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架&#xff0c;超轻量级、高度提炼&#xff0c;一套API优雅支持UDP 、TCP 、WebSocket 三种协议&#xff0c;支持iOS、Android、H5、小程序、Uniapp、标准Java平台&#xff0c;服务端基于Netty编写。 工…