关于@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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值