@synchronized是线程同步锁,易用、可读性高。

@synchronized(self) {
  临界区
}

利用如下命令将其重写

clang -rewrite-objc file 

得到C++实现

{
id _sync_obj = (id)self;
objc_sync_enter(_sync_obj);
struct _SYNC_EXIT {
_SYNC_EXIT(id arg) : sync_exit(arg) {}
~_SYNC_EXIT() {
objc_sync_exit(sync_exit);
}
id sync_exit;
} _sync_exit(_sync_obj);
}

深究objc_sync_enter和objc_sync_exit的源码

int objc_sync_enter(id obj)
{
if (obj) {
SyncData* data = id2data(obj, ACQUIRE);
recursive_mutex_lock(&data->mutex); // 获取递归锁
} else {
// @synchronized(nil) does nothing
}
}
int objc_sync_exit(id obj)
{
if (obj) {
SyncData* data = id2data(obj, RELEASE);
recursive_mutex_unlock(&data->mutex); // 释放递归锁
} else {
// @synchronized(nil) does nothing
}
}

可以看到

1、@synchronized底层用的是递归锁,即同个线程可重入,而不会导致死锁。

2、@synchronized(nil)是不会上锁的。

接着,显然SyncList是单链表,SyncData是其中的节点,而整体的存储则是一个“拉链哈希表”。

typedef struct SyncData {
struct SyncData* nextData; // 指向下一个SyncData节点
DisguisedPtr<objc_object> object; // @synchronized的参数,即上面的self
recursive_mutex_t mutex; // 递归锁
} SyncData; struct SyncList {
SyncData *data; // 单链表的头指针
spinlock_t lock; // 自旋锁,性能好,但不绝对安全
SyncList() : data(nil) { }
};
#define LOCK_FOR_OBJ(obj) sDataLists[obj].lock // 根据参数获取单链表的锁
#define LIST_FOR_OBJ(obj) sDataLists[obj].data // 根据参数获取单链表
static StripedMap<SyncList> sDataLists; // 哈希表
// 根据参数获取对应的SyncData节点,其实就是哈希表查找
static SyncData* id2data(id object, enum usage why)
{
spinlock_t *lockp = &LOCK_FOR_OBJ(object);
SyncData **listp = &LIST_FOR_OBJ(object); // 参数对应的单链表
SyncData* result = NULL; lockp->lock();
{
SyncData* p;
// 遍历单链表
for (p = *listp; p != NULL; p = p->nextData) {
if ( p->object == object ) {
// 找到参数对应的SyncData节点
result = p;
goto done;
}
}
}
// 单链表中还没有参数对应的SyncData节点
result = (SyncData*)calloc(sizeof(SyncData), 1);
result->object = (objc_object *)object; // 填充数据
new (&result->mutex) recursive_mutex_t();
   // 新建的SyncData节点往链表头部加
result->nextData = *listp;
*listp = result;
done:
lockp->unlock();
return result;
}
template<typename T>
class StripedMap {
static unsigned int indexForPointer(const void *p) {
// 取参数地址的哈希值作为数组的index
uintptr_t addr = reinterpret_cast<uintptr_t>(p);
return ((addr >> 4) ^ (addr >> 9)) % StripeCount;
}
public:
T& operator[] (const void *p) {
return array[indexForPointer(p)].value;
}
};   

可以看出,参数和递归锁是一一对应的。因此,一定要保证参数不变,这里的不变,指的是完全不变,包括地址。

源码:

https://opensource.apple.com/source/objc4/objc4-680/runtime/objc-sync.mm

@synchronized深入理解的更多相关文章

  1. 对于synchronized的理解

    一.synchronized 同步关键字,分为同步代码块和同步函数 二.对synchronized的理解(未加static关键字)(以下所说:对同步方法和同步代码块均适用) 对象的创建是以类为模板的 ...

  2. Java中synchronized关键字理解

    好记性不如烂笔头~~ 并发编程中synchronized关键字的地位很重要,很多人都称它为重量级锁.利用synchronized实现同步的基础:Java中每一个对象都可以作为锁.具体表现为以下三种形式 ...

  3. synchronized的理解

    用法解释 synchronized是Java中的关键字,是一种同步锁.它修饰的对象有以下几种: 1. 修饰一个代码块,被修饰的代码块称为同步语句块,其作用的范围是大括号{}括起来的代码,作用的对象是调 ...

  4. Java线程同步synchronized的理解

    JVM中(留神:马上讲到的这两个存储区只在JVM内部与物理存储区无关)存在一个主内存(Main Memory),Java中所有的变量存储在主内存中,所有实例和实例的字段都在此区域,对于所有的线程是共享 ...

  5. synchronized(this) 与 synchronized(class) 理解

    1.概念 synchronized 是 Java 中的关键字,是利用锁的机制来实现同步的. 锁机制有如下两种特性: 互斥性:即在同一时间只允许一个线程持有某个对象锁,通过这种特性来实现多线程中的协调机 ...

  6. java并发编程--Synchronized的理解

    synchronized实现锁的基础:Java中每一个对象都可以作为锁,具体表现为3种形式. (1)普通同步方法,锁是当前实例对象 (2)静态同步方法,锁是当前类的Class对象 (3)同步方法块,锁 ...

  7. 【多线程与高并发原理篇:4_深入理解synchronized】

    1. 前言 越是简单的东西,在深入了解后发现越复杂.想起了曾在初中阶段,语文老师给我们解说<论语>的道理,顺便给我们提了一句,说老子的无为思想比较消极,学生时代不要太关注.现在有了一定的生 ...

  8. volatile synchronized AtomicInteger的区别

    在网络上看了很多关于他们两个的区别与联系,今天用自己的话表述一下: synchronized 容易理解,给一个方法或者代码的一个区块加锁,那么需要注意的是,加锁的标志位默认是this对象,当然聪明的你 ...

  9. Synchronized用法原理和锁优化升级过程(面试)

    简介 多线程一直是面试中的重点和难点,无论你现在处于啥级别段位,对synchronized关键字的学习避免不了,这是我的心得体会.下面咱们以面试的思维来对synchronized做一个系统的描述,如果 ...

随机推荐

  1. html页面边框的简单设置方法

    <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title> ...

  2. WPF通过附加属性控制窗口关闭

    场景1 当使用 ShowDialog() 方式显示窗口时,通过定义附加属性的方式可实现在 ViewModel 中进行数据绑定(bool?)来控制子窗口的显示和关闭 public class ExWin ...

  3. 【pygame游戏编程】第五篇-----动画显示

    import pygame import sys import os pygame.init() #窗口居中 os.environ[' screen_width = 600 screen_high = ...

  4. JS闭包和引用

    简介 Javascript 中一个最重要的特性就是闭包的使用.因为闭包的使用,当前作用域总可以访问外部的作用域.因为Javascript 没有块级作用域,只有函数作用域,所以闭包的使用与函数是紧密相关 ...

  5. 【读书笔记】iOS-UI Automation 需要遵守的规则

    1,被测试的应用程序必须是Developer签名的应用程序或者是运行在模拟器里面的应用程序. 2,在被测试的应用程序开发的过程中需要处理UI控件的可访问性.使用IB的开发工程师需要在XIB中加入一个A ...

  6. VUE组件 之 Toast (Vue.extend 方式)

    一.效果图 二.说明 这类提示框组件我们通常都会直接在 JS 代码中进行调用.像下面这样: this.$toast('别点啦,到头啦!') 但看到网上大多数还是通过 component  方式实现的, ...

  7. Java并发编程(十一)线程池的使用

    1.new Thread的弊端如下: a. 每次new Thread新建对象性能差. b. 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或oom. c. 缺乏更多 ...

  8. Java并发编程(十)阻塞队列

    使用非阻塞队列的时候有一个很大问题就是:它不会对当前线程产生阻塞,那么在面对类似消费者-生产者的模型时,就必须额外地实现同步策略以及线程间唤醒策略,这个实现起来就非常麻烦.但是有了阻塞队列就不一样了, ...

  9. Expo大作战(三十九)--expo sdk api之 DocumentPicker,Contacts(获取手机联系人信息),Branch

    简要:本系列文章讲会对expo进行全面的介绍,本人从2017年6月份接触expo以来,对expo的研究断断续续,一路走来将近10个月,废话不多说,接下来你看到内容,讲全部来与官网 我猜去全部机翻+个人 ...

  10. (网页)textarea去掉回车换行

    转自CSDN: 1,把textarea内输入的内容中有回车换行的转成<br />传给后台, var content = $("#text").val().replace ...