java中的synchronized和linux系统的futex到底什么个关系?

简介: 首先,futex不是个完整的锁,它是“支持实现userspace的锁的building block“。也就是说,如果你想实现一个mutex,但不想把整个mutex都弄到内核里面去,可以通过futex来实现。但futex本身主要就是俩系统调用futex_wait和futex_wake.为了更好的解释这个问题,这里先梳理下锁本身是怎么工作的。

首先,futex不是个完整的锁,它是“支持实现userspace的锁的building block“。也就是说,如果你想实现一个mutex,但不想把整个mutex都弄到内核里面去,可以通过futex来实现。但futex本身主要就是俩系统调用futex_wait和futex_wake.


image.png


为了更好的解释这个问题,这里先梳理下锁本身是怎么工作的。


一个完整的锁需要解决几个问题:


争抢到一个内存,如果抢到了就算是得到了锁,可以继续干活;


如果没抢到,可以选择:


继续抢(spin)


调用某个系统调用把自己挂起来排队


别的线程释放锁后,会通知排队挂起来的一个或几个线程。醒过来的线程再去重复第一步。


早期的锁,所有这些步骤都是内核态的。但后来大家发现,步骤1用CAS在用户态就可以干了。而多线程大部分的时候抢锁都是没有竞争的,一抢就能抢到。一下子没抢到多抢几次大概率也能抢到了。


那么能不能用一直做spin,永远不做2.2和3呢?答案是不行的。如果遇到了竞争,这也就意味着大量空耗CPU。


因此后来的锁的设计一般都优化成了这样:


1. 在用户态写一段代码来抢锁,典型的实现是用CAS把一个指定的变量从0变成1。如果抢到了就结束了。此时是用户态的。


2. 如果抢不到,就看看是不是锁的持有者就是自己。如果是,也算是抢到了(当然要对变量做特殊的标记)。否则就spin几次重新抢。这也是用户态的。


3. 如果重试了N次,实在抢不到,此时调用futex_wait进入内核态,去把自己挂起+排队,等着被释放锁的线程futex_wake。


所以只有3进入内核态了。考虑到大部分情况都不是竞争很激烈的情况下,3根本就不用做。这样的锁的设计避免了由于系统调用导致的上下文切换,无疑很大的提高了效率。


Ok, 回到Java。Java的synchronized用JVM的monitor实现。而monitor实现内部用到了pthread_mutex和pthread_cond。这俩是pthread标准接口,实现在glibc里。而这俩的内部实现在Linux上目前都用到了futex。所以整体可以理解为futex帮助Java在Linux上实现了synchronized在内核那部分阻塞的功能;同时用户态的抢锁,重入控制等功能由JVM自己实现。两块代码共同提供了完整的synchronized功能。


所以当我们随便写一段synchornized会阻塞的Java代码:


publicclassTestFutex{
privateInteger a =newInteger(1);
synchronizedvoidshowA(){
System.out.println(a);try{
Thread.sleep(3000);
}catch(InterruptedException e) {
}    }classTextendsThread{
@Override
publicvoidrun(){
showA();        }    }publicTnewThread(){
returnnewT();
}publicstaticvoidmain(String[] args){
TestFutex tf =newTestFutex();
T t1 = tf.newThread();        T t2 = tf.newThread();        t1.start();        t2.start();    } }
并用strace去查看效果,你就会看到:
root@ba32a8cedf75:/test# strace -e futex java TestFutex
futex(0x7f8dacc130c8, FUTEX_WAKE_PRIVATE, 2147483647) = 0
futex(0x7f8dad64e9d0, FUTEX_WAIT, 97, NULL1
……
顺便说一句,基于AQS实现的JUC的那些ReentrantLock,Semaphore等内部也是类似的。其LockSupport.park内部用的也是这套东西。




相关文章
|
11月前
|
Ubuntu Linux Anolis
Linux系统禁用swap
本文介绍了在新版本Linux系统(如Ubuntu 20.04+、CentOS Stream、openEuler等)中禁用swap的两种方法。传统通过注释/etc/fstab中swap行的方式已失效,现需使用systemd管理swap.target服务或在/etc/fstab中添加noauto参数实现禁用。方法1通过屏蔽swap.target适用于新版系统,方法2通过修改fstab挂载选项更通用,兼容所有系统。
954 3
Linux系统禁用swap
|
11月前
|
Linux
Linux系统修改网卡名为eth0、eth1
在Linux系统中,可通过修改GRUB配置和创建Udev规则或使用systemd链接文件,将网卡名改为`eth0`、`eth1`等传统命名方式,适用于多种发行版并支持多网卡配置。
1559 3
|
11月前
|
JavaScript Java 大数据
基于JavaWeb的销售管理系统设计系统
本系统基于Java、MySQL、Spring Boot与Vue.js技术,构建高效、可扩展的销售管理平台,实现客户、订单、数据可视化等全流程自动化管理,提升企业运营效率与决策能力。
|
10月前
|
移动开发 监控 小程序
java家政平台源码,家政上门清洁系统源码,数据多端互通,可直接搭建使用
一款基于Java+SpringBoot+Vue+UniApp开发的家政上门系统,支持小程序、APP、H5、公众号多端互通。涵盖用户端、技工端与管理后台,支持多城市、服务分类、在线预约、微信支付、抢单派单、技能认证、钱包提现等功能,源码开源,可直接部署使用。
765 24
|
10月前
|
设计模式 消息中间件 传感器
Java 设计模式之观察者模式:构建松耦合的事件响应系统
观察者模式是Java中常用的行为型设计模式,用于构建松耦合的事件响应系统。当一个对象状态改变时,所有依赖它的观察者将自动收到通知并更新。该模式通过抽象耦合实现发布-订阅机制,广泛应用于GUI事件处理、消息通知、数据监控等场景,具有良好的可扩展性和维护性。
691 8
|
10月前
|
安全 前端开发 Java
使用Java编写UDP协议的简易群聊系统
通过这个基础框架,你可以进一步增加更多的功能,例如用户认证、消息格式化、更复杂的客户端界面等,来丰富你的群聊系统。
347 11
|
10月前
|
机器学习/深度学习 人工智能 自然语言处理
Java与生成式AI:构建内容生成与创意辅助系统
生成式AI正在重塑内容创作、软件开发和创意设计的方式。本文深入探讨如何在Java生态中构建支持文本、图像、代码等多种生成任务的创意辅助系统。我们将完整展示集成大型生成模型(如GPT、Stable Diffusion)、处理生成任务队列、优化生成结果以及构建企业级生成式AI应用的全流程,为Java开发者提供构建下一代创意辅助系统的完整技术方案。
466 10
|
Ubuntu Linux 网络安全
Linux系统初始化脚本
一款支持Rocky、CentOS、Ubuntu、Debian、openEuler等主流Linux发行版的系统初始化Shell脚本,涵盖网络配置、主机名设置、镜像源更换、安全加固等多项功能,适配单/双网卡环境,支持UEFI引导,提供多版本下载与持续更新。
1036 3
Linux系统初始化脚本
|
10月前
|
人工智能 监控 Java
Java与AI智能体:构建自主决策与工具调用的智能系统
随着AI智能体技术的快速发展,构建能够自主理解任务、制定计划并执行复杂操作的智能系统已成为新的技术前沿。本文深入探讨如何在Java生态中构建具备工具调用、记忆管理和自主决策能力的AI智能体系统。我们将完整展示从智能体架构设计、工具生态系统、记忆机制到多智能体协作的全流程,为Java开发者提供构建下一代自主智能系统的完整技术方案。
1153 4