博客
关于我
java中的synchronized和linux系统的futex到底什么个关系?
阅读量:89 次
发布时间:2019-02-25

本文共 1876 字,大约阅读时间需要 6 分钟。

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

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

 

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

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

  1. 争抢到一个内存,如果抢到了就算是得到了锁,可以继续干活;
  2. 如果没抢到,可以选择:
    1. 继续抢(spin)
    2. 调用某个系统调用把自己挂起来排队
  3. 别的线程释放锁后,会通知排队挂起来的一个或几个线程。醒过来的线程再去重复第一步。

早期的锁,所有这些步骤都是内核态的。但后来大家发现,步骤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代码:

public class TestFutex {     private Integer a = new Integer(1);      synchronized void showA() {         System.out.println(a);         try {             Thread.sleep(3000);         } catch(InterruptedException e) {          }     }     class T extends Thread {         @Override         public void run() {             showA();         }     }      public T newThread() {         return new T();     }      public static void main(String[] args) {         TestFutex tf = new TestFutex();         T t1 = tf.newThread();         T t2 = tf.newThread();         t1.start();         t2.start();     } }

并用strace去查看效果,你就会看到:

root@ba32a8cedf75:/test# strace -e futex java TestFutexfutex(0x7f8dacc130c8, FUTEX_WAKE_PRIVATE, 2147483647) = 0futex(0x7f8dad64e9d0, FUTEX_WAIT, 97, NULL1……

 

顺便说一句,基于AQS实现的JUC的那些ReentrantLock,Semaphore等内部也是类似的。其LockSupport.park内部用的也是这套东西。

转载地址:http://gan.baihongyu.com/

你可能感兴趣的文章
Node-RED中使用node-random节点来实现随机数在折线图中显示
查看>>
Node-RED中使用node-red-browser-utils节点实现选择Windows操作系统中的文件并实现图片预览
查看>>
Node-RED中使用node-red-contrib-image-output节点实现图片预览
查看>>
Node-RED中使用node-red-node-ui-iframe节点实现内嵌iframe访问其他网站的效果
查看>>
Node-RED中使用Notification元件显示警告讯息框(温度过高提示)
查看>>
Node-RED中使用range范围节点实现从一个范围对应至另一个范围
查看>>
Node-RED中实现HTML表单提交和获取提交的内容
查看>>
Node-RED中将CSV数据写入txt文件并从文件中读取解析数据
查看>>
Node-RED中建立TCP服务端和客户端
查看>>
Node-RED中建立Websocket客户端连接
查看>>
Node-RED中建立静态网页和动态网页内容
查看>>
Vue3+Element-ul学生管理系统(第二十二课)
查看>>
Node-RED中根据HTML文件建立Web网站
查看>>
Node-RED中解析高德地图天气api的json数据显示天气仪表盘
查看>>
Node-RED中连接Mysql数据库并实现增删改查的操作
查看>>
Node-RED中通过node-red-ui-webcam节点实现访问摄像头并截取照片预览
查看>>
Node-RED中配置周期性执行、指定时间阶段执行、指定时间执行事件
查看>>
Node-RED安装图形化节点dashboard实现订阅mqtt主题并在仪表盘中显示温度
查看>>
Node-RED怎样导出导入流程为json文件
查看>>
Node-RED订阅MQTT主题并调试数据
查看>>