关于@synchronized
一、结论
1)@synchronized内部使用的是recursive_mutex_lock,也就是递归锁,对于统一线程来说,@synchronized加锁的方法可以重复加锁。
比如代码:
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
[self testA];
}
- (void)testA
{
@synchronized(self)
{
NSLog(@"AAAAAA");
[self testB];
}
}
- (void)testB
{
@synchronized(self)
{
NSLog(@"BBBBBB");
}
}
输出结果为:
2018-08-21 15:25:51.058333+0800 TestSynchronized2[17367:7864821] AAAAAA
2018-08-21 15:25:51.058372+0800 TestSynchronized2[17367:7864821] BBBBBB
2)@synchronized 可以看成一个函数,加锁的对象是后面传入对象的地址,所以如果加锁对象重新赋值,那么地址会重新该表导致加锁失效。
如果这个对象为nil,那么等于没有加锁。
内部实现源代码如下:
static void _I_Demo_synchronizedTest(Demo * self, SEL _cmd) {
NSMutableArray *arr;
{
id _sync_obj = (id)arr;
objc_sync_enter(_sync_obj); // 同步锁进入,参数是arr
try {
struct _SYNC_EXIT {
_SYNC_EXIT(id arg) : sync_exit(arg) {}
~_SYNC_EXIT() {objc_sync_exit(sync_exit); // 同步锁退出,参数是arr
}
id sync_exit;
} _sync_exit(_sync_obj);// 调用结构体的构造函数,参数是arr
} catch (id e) {
}
}
}
int objc_sync_enter(id obj)
{
int result = OBJC_SYNC_SUCCESS;
if (obj) {
// 根据obj获取对应的SyncData节点,id2data函数在下面有解析
SyncData* data = id2data(obj, ACQUIRE);// 上锁
result = recursive_mutex_lock(&data->mutex); }
else
{ // @synchronized(nil) does nothing
}
return result;
}
typedef struct SyncData {
struct SyncData* nextData; // 指向下一个SyncData节点的指针
DisguisedPtr<objc_object> object; // @synchronized的参数obj
int32_t threadCount; // number of THREADS using this block
recursive_mutex_t mutex; // 递归锁
} SyncData;
struct SyncList {
SyncData *data; // 单链表头指针
spinlock_t lock; // 保证多线程安全访问该链表
SyncList() : data(nil) { }
};
static StripedMap<SyncList> sDataLists; // 哈希表,key:obj,value:单链表
// 根据obj获取对应的SyncData节点static SyncData* id2data(id object, enum usage why)
{
spinlock_t *lockp = &LOCK_FOR_OBJ(object); // SyncList锁
SyncData **listp = &LIST_FOR_OBJ(object); // obj对应的SyncData节点所在的
SyncList SyncData* result = NULL;// 这里省略一大坨cache代码
lockp->lock();
{
SyncData* p;
SyncData* firstUnused = NULL;
// 遍历单链表
for (p = *listp; p != NULL; p = p->nextData) {
if ( p->object == object ) {
// 找到obj对应的SyncData节点
result = p;
// SyncData节点对应的线程数加1
OSAtomicIncrement32Barrier(&result->threadCount);
goto done;
}
// SyncData节点对应的递归锁没有线程在用了,回收重用,可以节省节点创建的时间和空间
if ( (firstUnused == NULL) && (p->threadCount == 0) )
firstUnused = p;
}
// 链表中还没有obj对应的SyncData节点,但是有可重用的SyncData节点
// an unused one was found, use it
if ( firstUnused != NULL ) {
result = firstUnused;
result->object = (objc_object *)object;
result->threadCount = 1;
goto done;
}
}
// 链表中还没有obj对应的SyncData节点,而且没有可重用的SyncData节点
result = (SyncData*)calloc(sizeof(SyncData), 1);
result->object = (objc_object *)object;
result->threadCount = 1;
new (&result->mutex) recursive_mutex_t();
// 新建的SyncData节点往链表头部加
result->nextData = *listp;
*listp = result;
done:
lockp->unlock();
return result;}
3)swift中没有对应的方法,但是依然可以使用OC中调用加锁的函数,实现如下
func synchronized<T>(_ lock: AnyObject, _ body: () throws -> T) rethrows -> T {
objc_sync_enter(lock)
defer { objc_sync_exit(lock) }
return try body()
}
参考资料:
作者:悲观患者
链接:https://www.jianshu.com/p/d83b3b7d5a5a
关于@synchronized的更多相关文章
- java 多线程 Synchronized方法和方法块 synchronized(this)和synchronized(object)的理解
synchronized 关键字,它包括两种用法:synchronized 方法和 synchronized 块. 1. synchronized 方法:通过在方法声明中加入 synchronized ...
- 单例模式中用volatile和synchronized来满足双重检查锁机制
背景:我们在实现单例模式的时候往往会忽略掉多线程的情况,就是写的代码在单线程的情况下是没问题的,但是一碰到多个线程的时候,由于代码没写好,就会引发很多问题,而且这些问题都是很隐蔽和很难排查的. 例子1 ...
- Thread 学习记录 <1> -- volatile和synchronized
恐怕比较一下volatile和synchronized的不同是最容易解释清楚的.volatile是变量修饰符,而synchronized则作用于一段代码或方法:看如下三句get代码: int i1; ...
- synchronized使用说明
好久没有更新博客了,今天试着用简单的语言把synchronized的使用说清楚. synchronized是什么? synchronized是用来保证在多线程环境下代码同步执行的可重入的互斥锁.所谓互 ...
- 【Java并发系列04】线程锁synchronized和Lock和volatile和Condition
img { border: solid 1px } 一.前言 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制.这是Java并发编程中必须要理解的一个知识点.其实使用起来还是比 ...
- (转)Lock和synchronized比较详解
今天看了并发实践这本书的ReentantLock这章,感觉对ReentantLock还是不够熟悉,有许多疑问,所有在网上找了很多文章看了一下,总体说的不够详细,重点和焦点问题没有谈到,但这篇文章相当不 ...
- Synchronized同步性与可见性
Synchronized是具有同步性与可见性的,那么什么是同步性与可见性呢? (1)同步性:同步性就是一个事物要么一起成功,要么一起失败,可谓是有福同享有难同当,就像A有10000去银行转5000给身 ...
- 基于synchronized 或 ReadWriteLock实现 简单缓存机制
package cn.xxx.xxx; import java.util.HashMap; import java.util.Map; import java.util.concurrent.lock ...
- 【Java并发编程实战】-----synchronized
在我们的实际应用当中可能经常会遇到这样一个场景:多个线程读或者.写相同的数据,访问相同的文件等等.对于这种情况如果我们不加以控制,是非常容易导致错误的.在java中,为了解决这个问题,引入临界区概念. ...
- Lock、ReentrantLock、synchronized、ReentrantReadWriteLock使用
先来看一段代码,实现如下打印效果: 1 2 A 3 4 B 5 6 C 7 8 D 9 10 E 11 12 F 13 14 G 15 16 H 17 18 I 19 20 J 21 22 K 23 ...
随机推荐
- nginx重新整理——————https[七]
前言 简单介绍一些https. 正文 pki 公钥基础设施: 证书链: tls 通讯过程 验证身份 达成安全套件共识 传递秘钥 加密通讯 sudo yum install epel-release s ...
- leetcode每日一题:409. 最长回文串
409. 最长回文串 给定一个包含大写字母和小写字母的字符串,找到通过这些字母构造成的最长的回文串. 在构造过程中,请注意区分大小写.比如 "Aa" 不能当做一个回文字符串. 注意 ...
- 在windows电脑中安装redis
1,github下载地址:https://github.com/MSOpenTech/redis/tags 2,下载完成后,解压到对应文件夹 3,打开redis.windows.conf,在#requ ...
- SQLite总结
废话不多说: 优点,高并发读快速度读取超越所有主流大中型数据库 缺点,缺少同步机制,读写不能同时,且同时只能有一个写入线程 用途,硬盘式缓存 另附一SQLite工具类: import java.io. ...
- 同义词查找,关键词扩展,使用腾讯Tencent AILAB的800万词向量,gensim,annoy
最近在做一个关键词匹配系统,为了更好的效果, 添加一个关键词扩展的功能.使用Tencent AIlab的800万词向量文件. 腾讯AILAB的800万词向量下载地址:https://ai.tencen ...
- Kafka 集群副本数量调整
Kafka 创建时未指定多个副本或者副本数量过少,都可以在后期手动添加,另外如果副本过多也可以减少,当前调整基于 Kafka 的版本是 2.5.1,但是估计 2.1 ~ 2.5 应该都是兼容的. 下面 ...
- 【笔记】Oracle列转行unpivot&行转列 PIVOT
unpivot 说明:将表中多个列缩减为一个聚合列(多列转多行) 语法:unpivot(新列名 for 聚合列名 in (对应的列名1-列名n )) 写到了一个力扣的题,发现这个unpivot函数还没 ...
- 阿里云云原生加速器企业硬之城携手阿里云 Serverless 应用引擎(SAE)打造低代码平台
简介: 作为入选阿里云首期云原生加速器的企业,硬之城此前也获得了阿里云首批产品生态集成认证,通过云原生加速器项目携手阿里云共建更加丰富的云原生产业生态圈,加速云原生落地. 作者 | 陈泽涛(硬之城产品 ...
- Serverless JOB | 传统任务新变革
简介: SAE Job 重点解决了用户的效率和成本问题,在兼具传统任务使用体验和功能的同时按需使用,按量计费,做到低门槛任务上云,节省闲置资源成本. Job 作为一种运完即停的负载类型,在企业级开发中 ...
- OpenKruise 如何实现应用的可用性防护?
简介: OpenKruise 在 2021.9.6 发布了最新的 v0.10.0 版本新增了弹性拓扑管理和应用安全防护等能力,本文将为大家揭晓 OpenKruise 是如何实现应用的可用性防护能力. ...