@synchronized底层原理

synchronized是一种防止多线程操作引起不安全问题的锁。使用起来比较方便,而且不用管理它的生命周期,时常会被程序猿用到,但它的性能却是最差的......

各种锁的性能

为了更好的理解synchronized锁的性能,下面我们来分析下synchronized底层是怎么实现的。通过打印调用栈(bt)、增加符号断点、clang等方式,可以看出synchronized会被编译成objc_sync_enter函数,和objc_sync_exit,objc_sync_enter在libobjc.A.dylib中。

在libobjc.A.dylib中我们找到了函数objc_sync_enter。

objc_sync_enter函数

这里从源码备注可以知道参数obj就是synchronized传入的需要我们“保护”的数据,同时会创建递归互斥锁recursive mutex,既然是递归锁,那么就有重复调用以及可以重入的特性,锁的是需要“保护”的数据。既然是多线程操作数据,就有可能存在:

1、同一线程访问多次数据

2、多个线程访问多次数据

从objc_sync_enter函数里可知,需要“保护”的数据被封装成SyncData的一种数据,然后再进行加锁。查看SyncData数据:

SyncData结构体

SyncData里有recursive_mutex_t变量,查看定义可知(以下截图)

recursive_mutex_t

recursive_mutex_t就是一把递归锁,封装了lock和unlock的方法,这里引申出另一知识点:atomic内部也会使用os_unfair_recursive_lock。

回到objc_sync_enter函数,我们看下id2data函数是如果处理数据的。

id2data函数

这里增加一个知识点:线程局部存储(Thread Local Storage,TLS) 是操作系统为线程单独提供的私有空间,通常只有有限的容量。

TLS通过kvc能够获得线程。如119行:

SyncData *data = (SyncData *)tls_get_direct(SYNC_DATA_DIRECT_KEY)

通过SYNC_DATA_DIRECT_KEY能够获取到data,SyncData里面的object存储着需要“保护”的数据;

id2data函数的参数why由外部传入,objc_sync_enter时传的ACQUIRE,objc_sync_exit时传的RELEASE,通过代码可知,enter时候就会对lockCount进行+操作,exit时候进行-操作,通过lockCount知道数据被锁了多少次。而threadCount知道有多少个线程。下面我们列举下3种可能出现的多线程访问的情况:

1、第一次进入,没有锁的情况:tls和缓存里没有数据,直接进入202行,首先会在链表里去查找object(207行),如果是第一次进入并且在链表结构里没找到,就会进入223行,进行赋值,如果支持TLS进入256行,通过KVC存入result和lockCount。此时lockCount和threadCount都为1,为了安全和方便下次取数据,再存入缓存中(265行)这里的fetch_cache传入YES后会绑定当前线程,fetch_cache函数如下截图,返回result。

2、非第一次,但是同一个线程进入的情况:如果支持tls会在119行找到数据,找到后会lockCount++(135行),然后再存入tls里,然后返回result。

3、非第一次,不同线程进入:会进入160行,之前通过fetch_cache存入的数据现在就能找到了(第1种情况),然后对lockCount++(175行)后,再break。来到207行,会在链表结构里去查找到相同的需要“保护”的对象,找到后进行赋值,并threadCount++。最后的goto done跟第1种情况流程一样。

链表的查询和缓存的查找都是synchronized耗时的原因。

fetch_cache函数
全局线程空间与链表结构
objc_sync_exit函数

通过Clang知道,在调用objc_sync_enter后,会调用objc_sync_exit函数(如上图),objc_sync_exit函数也会调用id2data,此时传入RELEASE参数后,会对lockCount--。

总结:通过lockCount和threadCount可以知道需要“保护”的数据加锁了多少次,总共有多少线程访问。

SyncList的hash结构和SyncData的链表结构存储了不同线程、多次访问数据时的数据,包括lockCount和threadCount等数据,每个SyncData代表锁的次数。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,377评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,390评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,967评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,344评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,441评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,492评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,497评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,274评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,732评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,008评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,184评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,837评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,520评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,156评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,407评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,056评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,074评论 2 352

推荐阅读更多精彩内容