The obj used by #synchronized @synchronized(obj) is the unique identifier of the lock. Only when the identifier is the same, can the mutual exclusion be satisfied. If @synchronized(obj) in thread 2 is changed to @synchronized(self), thread 2 will not block. The advantage of @synchronized is that you don’t need to explicitly create a lock object in your code to implement the locking mechanism. However, as a precaution, the @synchronized block implicitly protects the code by adding an exception handler that automatically releases the mutex when an exception is thrown. So if you don’t want the extra overhead of implicit exception-handling routines, you can consider using lock objects.

- (void)synchronizedLock {
    NSObject * obj = NSObject.alloc.init;
    dispatch_async(dispatch_get_global_queue(0, 0), ^{
        @synchronized(obj) {
            NSLog(@"Operation 1 requiring thread synchronization begins");
            sleep(3);
            NSLog(@"Operation 1 requiring thread synchronization ends"); }}); dispatch_async(dispatch_get_global_queue(0, 0), ^{ sleep(1); @synchronized(obj) { NSLog(@"Operation requiring thread synchronization 2"); }}); /** Thread synchronization operation 1 start thread synchronization operation 1 end thread synchronization operation 2 */Copy the code

#NSLock NSLock is the most basic lock object Cocoa provides, and it’s the one we use most often. In addition to lock and unlock methods, NSLock also provides two methods: tryLock and **lockBeforeDate:**. The first method attempts to lock. If the lock is not available (it is already locked), the thread is not blocked and NO is returned. The **lockBeforeDate:** method tries to lock before the specified Date, and returns NO if the lock cannot be held before the specified time.

- (void)nslockLock {
    NSLock * lock = NSLock.alloc.init;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
//        [lock lock];
        [lock lockBeforeDate:NSDate.date];
        NSLog(@"Operation 1 requiring thread synchronization begins");
        sleep(2);
        NSLog(@"Operation 1 requiring thread synchronization ends");
        [lock unlock];
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        if(lock.tryLock) {// Try to obtain the lock, if not return NO, will not block NSLog(@"Unlocked");
            [lock unlock];
        }else {
            NSLog(@"Locked up.");
        }
        
        NSDate *date = [NSDate.alloc initWithTimeIntervalSinceNow:3];
        if ([lock lockBeforeDate:date]) {
            NSLog(@"No timeout, lock obtained.");
            [lock unlock];
        }
        else {
            NSLog(@"Timed out, no lock obtained"); }}); /** Operation 1 that needs thread synchronization starts locked operation 1 that needs thread synchronization ends Without timeout, get lock */}Copy the code

If the value of the dsema semaphore is greater than 0, the thread continues to execute the following statement and decrement the semaphore value by 1; If desema is 0, then this function blocks the current thread waiting for timeout (timeout is dispatch_time_t, not integer or float). If desema is incremented by dispatch_semaphore_signal and the semaphore is acquired by the thread of dispatch_semaphore_wait, then proceed down and decrement the semaphore by one. If no semaphore is retrieved or the value of the semaphore remains 0 during the wait, the thread will automatically execute the subsequent statement after timeout. Dispatch_semaphore is a semaphore, but can also be used as a lock if the total number of signals is set to 1. It performs even better than pthread_mutex when there are no wait conditions, but much worse when there are wait conditions. Its advantage over OSSpinLock is that it does not consume CPU resources while waiting.

- (void)semaphoreLock { dispatch_semaphore_t signal = dispatch_semaphore_create(1); // dispatch_time_t = dispatch_time(DISPATCH_TIME_NOW, 3 * NSEC_PER_SEC); // lock dispatch_async(dispatch_get_global_queue(0, 0), ^{dispatch_semaphore_wait(signal, overTime); // the semaphore is reduced by 1 NSLog at sign"Operation 1 requiring thread synchronization begins");
        sleep(2);
        NSLog(@"Operation 1 requiring thread synchronization ends"); dispatch_semaphore_signal(signal); // semaphore + 1}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ sleep(1); dispatch_semaphore_wait(signal, overTime); dispatch_semaphore_signal(signal); }); /** Operations that require thread synchronization 1 Start operations that require thread synchronization 1 End operations that require thread synchronization 2 If the timeout duration is set to >2, the synchronization can be completed. If thread 1 has not completed its run, the following code will be automatically executed. If thread 1 has not completed its run, the following code will be automatically executed. Operation requiring thread synchronization 1 Start Operation requiring thread synchronization 2 Operation requiring thread synchronization 1 End */}Copy the code

NSRecursiveLock is a recursive lock that can be requested multiple times by the same thread without causing a deadlock. This is mainly used in circular or recursive operations. This code is a typical deadlock case. In our thread, RecursiveMethod is called recursively. So each time it enters the block, it adds a lock, and from the second time, since the lock is already in use and not unlocked, it waits for the lock to be unlocked, which causes a deadlock and the thread to block.

Use NSRecursiveLock. It allows the same thread to lock more than once without causing a deadlock. A recursive lock keeps track of how many times it is locked. Each successful lock must balance calls to unlock. Only when this balance is reached can the lock finally be released for use by other threads.

- (void)recursiveLock { // NSLock * lock = NSLock.alloc.init; / / using nslock deadlocks NSRecursiveLock * lock = NSRecursiveLock alloc. Init; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ static void (^RecursiveMethod)(int); RecursiveMethod = ^(int value) { [lock lock];if (value > 0) {
                NSLog(@"value = %@",@(value));
                sleep(1);
                RecursiveMethod(value - 1);
            }
            [lock unlock];
        };
        
        RecursiveMethod(6);
    });
    
    /**
     value = 6
     value = 5
     value = 4
     value = 3
     value = 2
     value = 1
     */
}
Copy the code

#NSConditionLock – (void)lock condition (NSInteger)condition; – (void)unlockWithCondition:(NSInteger)condition; The two conditions inform each other when they are the same.

  1. Condition = [[NSConditionLock alloc]initWithCondition:0];
  2. Obtain lock [self.condition lockWhenCondition:1];
  3. Unlock [self condition unlockWithCondition: 1);
- (void)conditionLockLock {
    NSConditionLock * lock = [NSConditionLock.alloc initWithCondition:0];
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (YES) {
            [lock lockWhenCondition:1];
            NSLog(@"Operation 1 requiring thread synchronization begins");
            sleep(2);
            NSLog(@"Operation 1 requiring thread synchronization ends"); [lock unlockWithCondition:0]; }}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while (YES) {
            [lock lockWhenCondition:0];
            NSLog(@"Operation 2 requiring thread synchronization begins");
            sleep(1);
            NSLog(@"Operation 2 requiring thread synchronization ends"); [lock unlockWithCondition:2]; }}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while (YES) {
            [lock lockWhenCondition:2];
            NSLog(@"Operation 3 requiring thread synchronization begins");
            sleep(1);
            NSLog(@"Operation 3 requiring thread synchronization ends"); [lock unlockWithCondition:1]; }}); /** Operations requiring thread synchronization 2 Start operations requiring thread synchronization 2 End operations requiring thread synchronization 3 Start operations requiring thread synchronization 3 End operations requiring thread synchronization 1 Start operations requiring thread synchronization 1 End */Copy the code

#NSContidion a basic conditional lock. Manually control the wait and signal threads. [condition lock]; When multiple threads access and modify the same data source at the same time, the data source can be accessed and modified only once at the same time. The commands of other threads need to wait outside the lock. **[condition wait]; ** makes the current thread wait **[condition signal]; ** The CPU signals the thread to stop waiting and continue executing

- (void)contidionLock {
    NSCondition * condition = NSCondition.alloc.init;
    NSMutableArray * products = NSMutableArray.array;
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        while (YES) {
            [condition lock];
            if (products.count == 0) {
                NSLog(@"wait for product");
                [condition wait];
            }
            [products removeObjectAtIndex:0];
            NSLog(@"custome a product"); [condition unlock]; }}); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{while (YES) {
            [condition lock];
            [products addObject:NSObject.alloc.init];
            NSLog(@"Produce a product, quantity: %ld",products.count); [condition signal]; [condition unlock]; sleep(2); }}); / * *wait forProduct produce a product, total amount: 1 Custome a productwait forCustome a product */}Copy the code

#pthread_mutex c language under the definition of multi-thread locking mode.

  1. pthread_mutex_init(pthread_mutex_t * mutex,const pthread_mutexattr_t attr); Initialize the lock variable mutex. Attr is the lock attribute and NULL is the default attribute.
  2. *pthread_mutex_lock(pthread_mutex_tmutex); * * lock
  3. *pthread_mutex_tylock(pthread_mutex_tmutex); ** locks, but unlike 2, returns EBUSY when the lock is already in use, instead of suspending and waiting.
  4. *pthread_mutex_unlock(pthread_mutex_tmutex); * * releases the lock
  5. *pthread_mutex_destroy(pthread_mutex_t*mutex); Release after use
- (void)pthread_mutexLock {
    __block pthread_mutex_t theLock ;
    pthread_mutex_init(&theLock, NULL);
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        pthread_mutex_lock(&theLock);
        NSLog(@"Operation 1 requiring thread synchronization begins");
        sleep(3);
        NSLog(@"Operation 1 requiring thread synchronization ends");
        pthread_mutex_unlock(&theLock);
        
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        sleep(1);
        pthread_mutex_lock(&theLock);
        NSLog(@"Operation requiring thread synchronization 2"); pthread_mutex_unlock(&theLock); }); /** Thread synchronization operation 1 start thread synchronization operation 1 end thread synchronization operation 2 */Copy the code

#pthread_mutex(recursive) This is a recursive lock that pthread_mutex uses to prevent deadlocks in recursive situations. Works like an NSRecursiveLock recursive lock. **pthread_mutex_init(&theLock, NULL); ** The above code will deadlock if the lock is initialized. If you use the form of a recursive lock, there is no problem.

PTHREAD_MUTEX_NORMAL The default type, which is a normal lock. When a thread locks, the rest of the thread will form a waiting queue, and after unlocking the first-in, first-out principle to obtain the lock. PTHREAD_MUTEX_ERRORCHECK checks for incorrect locks. If the same thread requests the same lock, EDEADLK is returned. Otherwise, the action is the same as normal locks. This ensures that no nested deadlocks occur when multiple locks are not allowed. The PTHREAD_MUTEX_RECURSIVE lock allows the same thread to successfully acquire the same lock multiple times and unlock it multiple times. PTHREAD_MUTEX_DEFAULT Applies to locks. It is the simplest type of lock. It only waits for unlocking and recontests.

- (void)pthread_mutexrecursiveLock { __block pthread_mutex_t theLock; pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); / / settypeNon-recursive pthread_mutex_init(&theLock, &attr); pthread_mutexattr_destroy(&attr); dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ static void (^RecursiveMethod)(int); RecursiveMethod = ^(int value) { pthread_mutex_lock(&theLock);if (value > 0) {
                NSLog(@"value = %@",@(value));
                sleep(1);
                RecursiveMethod(value - 1);
            }
            pthread_mutex_unlock(&theLock);
        };
        
        RecursiveMethod(5);
    });
    
    /**
     value = 5
     value = 4
     value = 3
     value = 2
     value = 1
     */
}
Copy the code

OSSpinLock OSSpinLock is the highest performance spin lock. The principle is very simple, just do while waiting. The downside is that it consumes a lot of CPU resources when waiting, so it is not suitable for longer tasks. However, YY Dashen recently explained that OSSpinLock is no longer safe in his blog. Please use it with caution. .

- (void)osspinLock {
    _block OSSpinLock theLock = OS_SPINLOCK_INIT;
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        OSSpinLockLock(&theLock);
        NSLog(@"Operation 1 requiring thread synchronization begins");
        sleep(3);
        NSLog(@"Operation 1 requiring thread synchronization ends");
        OSSpinLockUnlock(&theLock);
        
    });
    
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
        OSSpinLockLock(&theLock);
        sleep(1);
        NSLog(@"Operation requiring thread synchronization 2");
        OSSpinLockUnlock(&theLock);
        
    });
}
Copy the code

The efficiency comparison is as follows:

Attachment Demo: Various “locks” in iOS