synchronized的原理和Callable接口

目录

♫synchronized原理

♪锁升级

♪锁优化

 

♫Callable接口


♫synchronized原理

我们知道synchronized锁可以控制多个线程对共享资源的访问,两个线程针对同一变量访问就会产生阻塞等待。而synchronized锁并不是一成不变的,它会根据情况进行一次升级。

♪锁升级

JVM 将synchronized锁分为 无锁 → 偏向锁 → 轻量级锁 → 重量级锁 四种状态。
♩偏向锁

第一个尝试加锁的线程就会优先进入偏向锁状态。偏向锁并不是真正的加锁,只是给线程一个偏向锁的标记,如果一直没有其他线程尝试加锁,则等到synchronized执行完取消偏向锁标记即可,如果有其他线程尝试加锁,则再真在进行加锁,让其它线程进入阻塞等待。(类似前面介绍过的懒汉模式,能不加锁则不加)

举个例子:假设男主是一个锁, 女主是一个线程。如果只有这一个线程来使用这个锁,那么男主女主即使不领证结婚(避免了高成本操作),也可以一直幸福的生活下去。但是女配出现了,也尝试竞争男主,此时不管领证结婚这个操作成本多高,女主也势必要把这个动作完成了,让女配死心。

♩轻量级锁

有其他线程尝试加锁 ,偏向锁状态就被消除, 进入了轻量级锁状态 ( 自适应的自旋锁 )。这里的轻量级锁就是通过CAS实现的自旋锁,由于自旋锁需占用CPU不断尝试加锁,如果一直自旋会很浪费CPU资源,故synchronized会通过一个计数器,记录自旋次数,一定超出某个阈值就不再自旋。

♩重量级锁

如果竞争进一步激烈 , 自旋不能快速获取到锁状态 , 就会转换为重量级锁。这里的重量级锁就是使用OS提供的 mutex 锁,此时其它线程尝试加锁就是在内核态来判断锁是否被占用,如果没被占用则返回用户态,否则就进入等待队列,等待操作系统唤醒

♪锁优化

♩锁消除
锁消除是指编译器自动判断程序中某些代码块不需要同步保护,因而消除这些代码块中的锁,从而提高程序的执行效率。
锁消除的主要依据是:如果程序中某些代码块不会被多个线程同时访问,那么这些代码块中的锁就可以被消除,因为不需要花费额外的时间来进行同步操作。
如:StringBuffer的关键方法都带有synchronized, 但如果只是在单线程中执行这写方法, 那么这些加锁操作是没有必要的,此时编译器就会把加锁操作去掉。
♩锁粗化
锁粗化是指将多个连续的细粒度锁(synchronized包含的代码块较少)合并为一个粗粒度锁(synchronized包含的代码块较多),以减少锁竞争带来的性能开销。
实际开发过程中,使用细粒度锁,是期望释放锁的时候其他线程能使用锁。但是实际上可能并没有其他线程来抢占这个锁。这种情况 JVM 就会自动把锁粗化,避免频繁申请释放锁。
举个例子:
张三要打电话向老师问三个问题,打一次电话问一个问题,连续打了三次电话。锁优化相当于张三打一次电话就把三个问题一起问了。

 

♫Callable接口

Callable  是一个  interface,与 Runnable 类似,也是用来描述一个任务,不同的是 Runnable 描述的任务没有返回值,而 Callable 描述的任务有返回值。如果需要一个线程单独计算出某结果,Callable是比较合适的。
如:创建线程计算 1 + 2 + 3 + ... + 1000, 不使用 Callable 版本:
①.创建一个类 Result, 包含一个 sum 表示最终结果, lock 表示线程同步使用的锁对象。
②.main 方法中先创建 Result 实例, 然后创建一个 Runable,在 Runnable 里计算1 + 2 + 3 + ... + 1000,将 Runnable 传入线程 thread。
③.主线程同时使用 wait 等待线程 thread  计算结束。  ( 注意: 如果执行到 wait 之前, 线程 thread  已经计算完了, 就不必等待了)。
④.当线程 thread  计算完毕后, 通过 notify 唤醒主线程, 主线程再打印结果。
public class Test {
    static class Result {
        public int num = 0;
        public Object lock = new Object();
    }

    public static void main(String[] args) throws InterruptedException {
        Result result = new Result();
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                int num = 0;
                for (int i = 1; i <= 1000; i++) {
                    num += i;
                }
                synchronized (result.lock) {
                    result.num = num;
                    result.lock.notify();
                }
            }
        };
        Thread thread = new Thread(runnable);
        thread.start();
        synchronized (result.lock) {
            while (result.num == 0) {
                result.lock.wait();
            }
            System.out.println(result.num);
        }
    }
}
可以看到, 上述代码需要一个辅助类 Result, 还需要使用一系列的加锁和 wait notify 操作, 代码复杂,容易出错。
创建线程计算 1 + 2 + 3 + ... + 1000, 使用 Callable 版本:
①.创建一个匿名内部类, 实现 Callable 接口, Callable 带有泛型参数, 泛型参数表示返回值的类型。
②.重写 Callable call 方法, 完成累加的过程, 直接通过返回值返回计算结果。
③.把 callable 实例使用 FutureTask 包装一下。
④.创建线程 , 线程的构造方法传入 FutureTask . 此时新线程就会执行 FutureTask 内部的 Callable 的 call 方法, 完成计算, 计算结果就放到了 FutureTask 对象中。
⑤.在主线程中调用 futureTask.get() 能够阻塞等待新线程计算完毕, 并获取到 FutureTask 中的结果。
public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Callable<Integer> callable = new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                int sum = 0;
                for (int i = 1; i <= 1000; i++) {
                    sum += i;
                }
                return sum;
            }
        };
        FutureTask<Integer> futureTask = new FutureTask<>(callable);
        Thread thread = new Thread(futureTask);
        thread.start();
        int result = futureTask.get();
        System.out.println(result);
    }
}
可以看到, 使用 Callable 和 FutureTask 之后, 代码简化了很多, 也不必手动写线程同步代码了。
注:
Callable 通常需要搭配 FutureTask 来使用,FutureTask 用来保存 Callable 的返回结果,因为 Callable 往往是在另一个线程中执行的,啥时候执行完并不确定,FutureTask 就可以负责这个等待结果出来的工作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/132160.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

自动化测试系列 —— UI自动化测试

UI 测试是一种测试类型&#xff0c;也称为用户界面测试&#xff0c;通过该测试&#xff0c;我们检查应用程序的界面是否工作正常或是否存在任何妨碍用户行为且不符合书面规格的 BUG。了解用户将如何在用户和网站之间进行交互以执行 UI 测试至关重要&#xff0c;通过执行 UI 测试…

自适应AI chatGPT智能聊天创作官网html源码/最新AI创作系统/ChatGPT商业版网站源码

源码简介&#xff1a; 自适应AI chatGPT智能聊天创作官网html源码&#xff0c;这是最新AI创作系统&#xff0c;作为ChatGPT商业版网站源码&#xff0c;它是支持创作、编写、翻译、写代码等。是一个智能聊天系统项目源码。 注意&#xff1a;这个只是网站html源码&#xff0c;要…

8年经验之谈 —— 如何用 JMeter 编写性能测试脚本?

Apache JMeter 应该是应用最广泛的性能测试工具。怎么用 JMeter 编写性能测试脚本&#xff1f; 1. 编写 HTTP 性能测试脚本 STEP 1. 添加 HTTP 请求 img STEP 2. 了解配置信息 HTTP 请求各项信息说明&#xff08;以 JMeter 5.1 为例&#xff09;。 如下图所示&#xff1a;…

第三章《补基础:不怕学不懂概率统计》笔记

3.1 什么是概率 概率亦称“或然率”&#xff0c;它反映随机事件出现的可能性大小&#xff0c;在现实生活中有着极其普遍的应用。 3.1.1 最简单的概率的例子 3.1.2 概率论与数理统计的关系 概率论与数理统计的关系可以概括为&#xff0c;概率论是数理统计的理论基础&#xf…

VSCode + PlatformIO ESP32开发环境配置(离线版5分钟搞定)

文章目录 安装python1. 打开应用商店2. 应用商店搜索python3. 安装python4. python安装完成5. 打开命令提示符6. 验证安装结果7. 更新pip源为国内源 安装VSCode下载VSCode安装Vscode安装简体中文插件安装VSCode platformio插件安装Prettier - Code formatter插件 &#xff08;建…

图书销售数据大屏可视化【可视化项目案例-03】

🎉🎊🎉 你的技术旅程将在这里启航! 🚀🚀 本文选自专栏:可视化技术专栏100例 可视化技术专栏100例,包括但不限于大屏可视化、图表可视化等等。订阅专栏用户在文章底部可下载对应案例源码以供大家深入的学习研究。 🎓 每一个案例都会提供完整代码和详细的讲解,不…

【C++那些事儿】类与对象(2)

君兮_的个人主页 即使走的再远&#xff0c;也勿忘启程时的初心 C/C 游戏开发 Hello,米娜桑们&#xff0c;这里是君兮_&#xff0c;我之前看过一套书叫做《明朝那些事儿》&#xff0c;把本来枯燥的历史讲的生动有趣。而C作为一门接近底层的语言&#xff0c;无疑是抽象且难度颇…

合同审查---财务条款、合同形式与生效审查

1.合同主体 1人 廖 2.财务条款、合同形式与生效 1人 黄 3.履行、验收、知识产权、不可抗力 1人 詹 4.违约责任、争议解决、保密、法律引用 1人 王 代码规范&#xff1a; 1.代码函数的层级 各审查点在json中分为3级层级&#xff0c;但用python写规则的时候&#xff0c;1级层级为…

2023年成为优秀自动化测试工程师的 7 个步骤!

“测试自动化测试工程师可以将你从充满代码的世界中拯救出来。”企业完全同意这一说法&#xff0c;这就是您在自动化测试行业中看到大量就业机会的原因。我在 Quora 上收到了很多与自动化测试中的职业选择相关的答案请求&#xff0c;以及人们如何在有或没有手动测试经验的情况下…

JVM源码剖析之软、弱、虚引用的处理细节

目录 写在前面&#xff1a; 源码剖析&#xff1a; Java层面&#xff1a; JVM层面&#xff1a; 使用危险点&#xff1a; 总结&#xff1a; 版本信息&#xff1a; jdk版本&#xff1a;jdk8u40 垃圾回收器&#xff1a;Serial new/old 写在前面&#xff1a; 不同的垃圾回收…

第23章(下)_索引原理剖析

文章目录 索引实现索引存储B树为什么 MySQL InnoDB 选择 B 树作为索引的数据结构&#xff1f;B 树层高问题关于自增id最左匹配原则覆盖索引索引下推innodb体系结构Buffer poolchange buffer 总结 索引实现 索引存储 innodb 由段、区、页组成。段分为数据段、索引段、回滚段等…

摊位展示预约小程序的作用有哪些

无论市场还是街边&#xff0c;小摊小贩往往很多&#xff0c;组成了丰富多彩的线下购物环境&#xff0c;而在实际发展中&#xff0c;摊位的需求度很高&#xff0c;但由于种种原因&#xff0c;导致在实际发展中&#xff0c;也有一定难题&#xff1a; 1、摊位预约难、信息查看难 …

js 求数组中的对象某个属性和

可以直接看下效果 代码&#xff1a; <script>let list [{num: 1,price: 10,},{num: 2,price: 10,},{num: 3,price: 10,},{num: 4,price: 10,},]// for循环 求总数和 num的和let num 0for (let i 0; i < list.length; i) {num list[i].num}console.log(第一种&am…

pytorch代码实现注意力机制之Flatten Attention

Flatten Attention 介绍&#xff1a;最新注意力Flatten Attention&#xff1a;聚焦的线性注意力机制构建视觉 Transformer 在将 Transformer 模型应用于视觉任务时&#xff0c;自注意力机制 (Self-Attention) 的计算复杂度随序列长度的大小呈二次方关系&#xff0c;给视觉任务…

Leetcode-144 二叉树的前序遍历

递归方法 /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* TreeNode(int val) { this.val val; }* TreeNode(int val, TreeNode left, TreeNode right) {* …

局部路由守卫component守卫

局部路由守卫component守卫 component守卫&#xff08;beforeRouteEnter、beforeRouteLeave&#xff09; 代码位置&#xff1a;在路由组件中&#xff0c;代码是写在component当中的&#xff08;XXX.vue&#xff09;beforeRouteEnter、beforeRouteLeave都有三个参数&#xff1…

2023年加氢工艺证考试题库及加氢工艺试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2023年加氢工艺证考试题库及加氢工艺试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考试大纲随机出的加氢…

Linux操作系统使用及C高级编程-D2软件包管理

有两种类型的软件包&#xff1a;二进制软件包(deb)和源码包(deb-src) 二进制软件包(Binary Packages)&#xff1a;包含可执行文件、库文件、配置文件、main/info页面、版权声明和其他文档 源码包(Source Packages)&#xff1a;包含软件源代码、版本修改说明、构建指令及编译工…

科研检测机构服务预约小程序的效果如何

科研检测机构涵盖的业务比较广&#xff0c;比如水质检测、农产品检测、食品检测等&#xff0c;对相关从业者来说&#xff0c;可能需要频繁使用这些业务&#xff0c;或者个人偶尔需要一些东西检测。 对用户和检测机构来说&#xff0c;由于行业的特殊性&#xff0c;在实际发展中…

男科医院服务预约小程序的作用是什么

医院的需求度从来都很高&#xff0c;随着技术发展&#xff0c;不少科目随之衍生出新的医院的&#xff0c;比如男科医院、妇科医院等&#xff0c;这使得目标群体更加精准&#xff0c;同时也赋能用户可以快速享受到服务。 当然相应的男科医院在实际经营中也面临痛点&#xff1a;…