关于@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 ...
随机推荐
- 【4】Spring框架的起源
在我们的<Java Spring框架入门教程>中对 Spring 框架进行了十分详尽的介绍和剖析,但在学习 Spring Boot 之前,在这里回顾一下 Spring 是怎么出现的. Sp ...
- redis 简单整理——内存的管理[二十六]
前言 redis 是一个内存型数据库,那么就需要重点关注一下内存了. 正文 理解Redis内存,首先需要掌握Redis内存消耗在哪些方面.有些内存消 耗是必不可少的,而有些可以通过参数调整和合理使用来 ...
- PIL.Image, numpy, tensor, cv2 之间的互转,以及在cv2在图片上画各种形状的线
''' PIL.Image, numpy, tensor, cv2 之间的互转 ''' import cv2 import torch from PIL import Image import num ...
- sql多表分页查询【oracle】
sql多表查询[oracle] 做个记录,好歹是写出来了,使用左连接的方法,进行四表查询,且使用rownum进行分页 把涉及内容的全部替换了,不过应该都看得懂,就不说了 select * from ( ...
- 力扣1127(MySQL)-用户购买平台(困难)
题目: 支出表: Spending 这张表记录了用户在一个在线购物网站的支出历史,该在线购物平台同时拥有桌面端('desktop')和手机端('mobile')的应用程序.这张表的主键是 (user_ ...
- Nacos2.0的K8s服务发现生态应用及规划
简介:Nacos 是阿里巴巴于 2018 年开源的注册中心及配置中心产品,帮助用户的分布式微服务应用进行服务发现和配置管理功能.随着 Nacos2.0 版本的发布,在性能和扩展性上取得较大突破后,社 ...
- 平安保险基于 SPI 机制的 RocketMQ 定制化应用
简介:本文讲讲述平安保险为何选择 RocketMQ,以及在确定使用消息中间件后,又是如何去选择哪款消息中间件的. 作者:孙园园|平安人寿资深开发 为什么选用 RocketMQ 首先跟大家聊聊我们为什 ...
- OpenKruise v0.9.0 版本发布:新增 Pod 重启、删除防护等重磅功能
简介: OpenKruise 是阿里云开源的云原生应用自动化管理套件,也是当前托管在 Cloud Native Computing Foundation (CNCF) 下的 Sandbox 项目.它来 ...
- iOS App 启动优化
简介: 作为程序猿来说,"性能优化"是我们都很熟悉的词,也是我们需要不断努⼒以及持续进⾏的事情:其实优化是⼀个很⼤的课题,因为细分来说的话有⼤⼤⼩⼩⼗⼏种优化⽅向 ,但是切忌在实 ...
- 使用 Kafka Assistant,为您的开发加速
简要介绍 快速查看所有 Kafka 集群,包括Brokers.Topics和Consumers 支持各种认证模式:PLAINTEXT.SASL_PLAINTEXT.SSL.SASL_SSL 对Kafk ...