线程间通信和线程互斥

线程间通信

 1> 线程间通信分为两种

  • 主线程进入子线程(前面的方法都可以)
  • 子线程回到主线程

 2> 返回主线程

 3> 代码

  这个案例的思路是:当我触摸屏幕时,会在子线程加载图片,然后在主线程刷新UI界面

  视图布局我就不写了,大家自己来吧,线程间通信代码如下:

#pragma mark - 添加响应方法触发创建子线程并加载数据
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    // 创建子线程
    [self performSelectorInBackground:@selector(loadImage) withObject:nil];
}

- (void)loadImage
{   NSLog(@"当前线程:%@", [NSThread currentThread]);     NSLog(@"主线程:%@", [NSThread mainThread]);
    NSString *urlStr = @"https://ss0.bdstatic.com/94oJfD_bAAcT8t7mm9GUKT-xh_/timg?image&quality=100&size=b4000_4000&sec=1463455875&di=ef2da3f0fe711b471966aa1511483d0b&src=http://img4.duitang.com/uploads/item/201308/20/20130820094450_rsmYi.jpeg";

    NSURL *url = [NSURL URLWithString:urlStr];

    NSURLRequest *request = [NSURLRequest requestWithURL:url];

    self.imageData = [NSURLConnection sendSynchronousRequest:request returningResponse:nil error:nil];

    // 返回主线程,刷新UI
    [self performSelectorOnMainThread:@selector(reloadImageView) withObject:nil waitUntilDone:YES];
}

- (void)reloadImageView
{
    // 进入主线程一般进行来安全判断
    if ([NSThread isMainThread]) {
        // 刷新UI
        UIImage *showIamge = [UIImage imageWithData:self.imageData];

        self.showImageView.image = showIamge;
    }
}

点击触发事件,开辟子线程成功,在主线程中刷新UI,图片显示成功

线程互斥

 1> 应用场景

  多线程并行编程中,线程间同步与互斥是一个很有技巧的也很容易出错的地方。

  多个线程操作同一个资源(即某个对象),需要保证线程在对资源的状态(即对象的成员变量)进行一些非原子性操作后,状态仍然正确。

  典型的例子是“售票厅售票应用”。售票厅剩余20张票,10个窗口去卖这些票。这10个窗口,就是10条线程,售票厅就是他们共同操作的资源,其中剩余的20张票就是这个资源的一个状态。线程买票的过程就是去递减这个剩余数量的过程。

  我们看看会发生什么问题

- (void)viewDidLoad {
    [super viewDidLoad];
    // 模拟买票系统
    // 一共20张票,10个窗口卖
    __block NSInteger count = 20;
    dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(ticketQueue, ^{
            //这里相当于每个窗口卖2张票
            for (int i = 0; i < 2; i ++) {
                NSLog(@"买到了第%ld张票",count);
                count--;
            }
        });
    }
}

运行的效果如下图,不同的售票窗口贩卖了同一张

 2> 线程互斥解决方案

  • 方法一 @synchronized 自动对参数对象加锁,保证临界区内的代码线程安全(最简单的方法)

  官方文档解释:

   The @synchronized directive is a convenient way to create mutex locks on the fly in Objective-C code.

  个人理解:

   @synchronized, 代表这个方法加锁, 相当于不管哪一个线程(例如线程A),运行到这个方法时,都要检查有没有其它线程例如B正在用这个方法,有的话要等正在使用synchronized方法 的线程B运行完这个方法后再运行此线程A,没有的话,直接运行。它包括两种用法:synchronized 方法和 synchronized 块。

   @synchronized
方法控制对类(一般在IOS中用在单例中)的访问:每个类实例对应一把锁,每个 synchronized
方法都必须获得调用该方法锁方能执行,否则所属就会发生线程阻塞,方法一旦执行,就独占该锁,直到从该方法返回时才将锁释放,此后被阻塞的线程方能获得该
锁,重新进入可执行状态。这种机制确保了同一时刻对于每一个类,至多只有一个处于可执行状态,从而有效避免了类成员变量的访问冲突(只要所有可能访问类的
方法均被声明为 synchronized)

- (void)viewDidLoad {
    [super viewDidLoad];
    // 模拟买票系统
    // 一共20张票,10个窗口卖
    __block NSInteger count = 20;
    __weak typeof(self) weakSelf = self;
    dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(ticketQueue, ^{
            // 给买票操作加锁,保证代码块只有一个线程访问
            @synchronized(weakSelf) {
                //这里相当于每个窗口卖2张票
                for (int i = 0; i < 2; i ++) {
                    NSLog(@"买到了第%ld张票",count);
                    count--;
                }
            }
        });
    }
}

  运行结果:

  • 方法二 NSLock

  官方文档解释:

   An NSLock object is used to coordinate the operation of multiple threads of execution within the same application. An NSLock object can be used to mediate access to an application’s global data or to protect a critical section of code, allowing it to run atomically.

  个人理解:

   在一个应用里面协调线程间的执行。

- (void)viewDidLoad {
    [super viewDidLoad];
    // 模拟买票系统
    // 一共20张票,10个窗口卖
    __block NSInteger count = 20;

    // 创建线程锁
    NSLock *lock = [[NSLock alloc]init];
    dispatch_queue_t ticketQueue = dispatch_queue_create("sell ticket", DISPATCH_QUEUE_CONCURRENT);
    for (int i = 0; i < 10; i ++) {
        dispatch_async(ticketQueue, ^{
            // 给买票操作加锁,保证代码块只有一个线程访问
            // 加锁,不会出现多个窗口同时卖一张票的情况
            [lock lock];
            //这里相当于每个窗口卖2张票
            for (int i = 0; i < 2; i ++) {
                NSLog(@"买到了第%ld张票",count);
                count--;
            }
            // 解锁
            [lock unlock];
        });
    }
}

  以上两种方法小编进行了使用,以下两种方法大家有兴趣可以试试,小编就不进行使用了

  • 方法三 NSConditionLock 条件锁 可以设置条件

  官方文档解释:

   The NSConditionLock class defines objects whose locks can be associated with specific, user-defined conditions. Using an NSConditionLock object, you can ensure that a thread can acquire a lock only if a certain condition is met. Once it has acquired the lock and executed the critical section of code, the thread can relinquish the lock and set the associated condition to something new. The conditions themselves are arbitrary: you define them as needed for your application.

  个人理解:

   根据条件加锁与解锁。

  • 方法四 NSRecursiveLock 递归锁 多次调用不会阻塞已获取该锁的线程

  官方文档解释:

   NSRecursiveLock defines a lock that may be acquired multiple times by the same thread without causing a deadlock, a situation where a thread is permanently blocked waiting for itself to relinquish a lock. While the locking thread has one or more locks, all other threads are prevented from accessing the code protected by the lock.

  个人理解:

    同一个线程可以多次请求加锁,但不会引起死锁。

时间: 2024-11-23 11:19:32

线程间通信和线程互斥的相关文章

Java笔记七.线程间通信与线程生命的控制

线程间通信与线程生命的控制 一.线程通信方法 Java是通过Object类的wait.notify.notifyAll这几个方法来实现进程键的通信.由于所有的类都是从Object继承的,因此在任何类中都可以直接使用这些方法. wait:告诉当前线程放弃监视器并进入睡眠状态,知道其他线程进入同一监视器并调用notify为止; notify:唤醒同一对象监视器中调用wait的第一个线程.用于类似饭馆有一个空位后通知所有等候就餐的顾客中的第一位可以入座的情况: notifyAll:唤醒同一对象监视器中

黑马程序员——JAVA基础之Day24 多线程 ,死锁,线程间通信 ,线程组,线程池,定时器。

------- android培训.java培训.期待与您交流! ---------- Lock()实现提供了比使用synchronized方法和语句可获得更广泛的锁定操作. private Lock lock =new ReentrantLock(); 被锁的代码要用   lock.lock()                lock.unlock()    包括.其中用try   ...finally包围 同步:效率低,如果出现同步嵌套,会出现死锁.  但是安全. 死锁问题:两个或者两个以上

浅析线程间通信一:互斥量和条件变量

线程同步的目的简单来讲就是保证数据的一致性.在Linux中,常用的线程同步方法有互斥量( mutex ).读写锁和条件变量,合理使用这三种方法可以保证数据的一致性,但值得的注意的是,在设计应用程序时,所有的线程都必须遵守相同的数据访问规则为前提,才能保证这些同步方法有效,如果允许某个线程在没有得到访问权限(比如锁)的情况下访问共享资源,那么其他线程在使用共享资源前都获得了锁,也会出现数据不一致的问题.另外还有自旋锁.barrier和信号量线程同步方法.本文将讨论互斥量和条件变量的使用,并给出了相

2. GCD的使用(线程间通信---子线程执行耗时操作/主线程更新UI)

// // ViewController.m // 07-线程间通信(加载图片,在主线程中更新UI) // // Created by Jasperay on 15/9/3. // Copyright (c) 2015年 @aLonelyRoot3. All rights reserved. // #import "ViewController.h" @interface ViewController () @property (weak, nonatomic) IBOutlet UI

线程的几个主要概念----线程间通信;线程死锁;线程控制:挂起、停止和恢复(线程同步的5种方式)

(一)线程同步(5种同步方式) 1.同步方法--->有synchronized关键字修饰的方法.(Java的每个内置对象都有一个内置锁,当用synchronized修饰方法--->内置锁保护整个方法) 在调用该方法前,需要获得内置锁,否则就处于阻塞状态. eg: public  synchronized  void  save(){     } [注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类.] 2.同步代码块:--->有synchron

java多线程——线程间通信之线程等待唤醒机制

三个方法 wait() notify() notifyAll() 三个方法都使用在同步中,因为要对持有锁(又叫监控)的线程操作. 所以要使用在同步中,因为只有同步才具有锁. 为什么这些操作线程的方法均出现在Object类中? 因为这些方法在操作同步中的线程时候,都必须要标识所操作线程识有锁.只有同一个锁上的被等待的线程,可以被同一个锁上的notify唤醒,不可以对不同锁中的线程进行唤醒. 也就是说,等待和唤醒必须是同一个锁. public class Demo1 { public static

线程的创建和线程间通信

线程的创建: #include<pthread.h> int pthread_create(pthread_t *tidp,const pthread_attr_t *attr,(void*)(*start_rtn)(void*),void *arg); /*创建线程 *参数:pthread_t *tidp :线程id,在Linux系统中是int,在Unix系统中是结构体,在移植性考虑还是使用pthread_t类型比较好. *const pthread_attr_t *attr:线程属性,主要

Java 里如何实现线程间通信

正常情况下,每个子线程完成各自的任务就可以结束了.不过有的时候,我们希望多个线程协同工作来完成某个任务,这时就涉及到了线程间通信了.本文涉及到的知识点:thread.join(), object.wait(), object.notify(), CountdownLatch, CyclicBarrier, FutureTask, Callable 等. 下面我从几个例子作为切入点来讲解下 Java 里有哪些方法来实现线程间通信. 如何让两个线程依次执行? 那如何让 两个线程按照指定方式有序交叉运

【黑马】程序员————多线程(二)单例设计模式、线程间通信,JDK1.5互斥锁

------Java培训.Android培训.iOS培训..Net培训.期待与您交流!----- 一.单例设计模式 单例设计模式的意义: A.保证类在内存中只有一个对象,不提供外部访问方式,构造函数用private修饰. B.提供公共方法(static修饰,类的静态方法),获取类的实例.单例设计模式分为饿汉和懒汉两种模式. 饿汉式&懒汉式 class Test33 { public static void main(String[] args) { Fanjianan.getInstance()