多线程安全问题及各种锁

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了多线程安全问题及各种锁相关的知识,希望对你有一定的参考价值。

参考技术A

多线程使用不当会出现资源竞争,比如多个线程同时对一块资源进行修改,就会很容易引发数据错乱和数据安全问题。

示例:
以购票系统为例,

对于多线程出现的这种问题,我们的解决办法就是使用线程同步技术,而常见的就是加锁。

2.1 OSSpinLock 自旋锁
自旋锁等待锁的线程会处于忙等(busy-wait)状态,一直占用着CPU资源。
并且现在已经不安全,可能出现优先级反转的问题。
如果等待锁的优先级较高,它会一直占用着CPU的资源,优先级低的线程就无法释放锁。
ios10被苹果废弃。由于已经不再安全, 这里不讨论用法了.

2.2 os_unfair_lock

导入头文件 #import <os/lock.h>

调用 ticketTest 方法
打印结果:

2.3 pthread_mutex 互斥锁

普通用法

导入头文件 #import <pthread.h>

调用 ticketTest 方法
打印结果:

mutex 也叫“互斥锁”,等待锁的线程处于休眠状态,不是忙等状态。

上面使用的是 Normal 类型同 OSSpinLock 和 os_unfair_lock 作用一样

递归用法
递归锁在被同一线程重复获取时不会产生死锁
递归类型 PTHREAD_MUTEX_RECURSIVE :

条件用法

2.4 NSLock、NSRecursiveLock
NSLock 是对 mutex 普通锁的封装
NSRecursiveLock 是对 mutex 递归锁的封装

调用 ticketTest 方法
打印结果:

NSRecursiveLock 用法与 NSLock 的用法一样。

2.5 NSCondition
NSCondition 是对 mutex 和 cond 的封装

2.6 NSConditionLock
它是对 NSCondition 的进一步封装,可以设置具体的条件值

2.7 dispatch_queue
直接使用GCD的串行队列,也可以实现线程同步

调用ticketTest方法
打印结果:

2.8 dispatch_semaphore
信号量的初始值可以控制线程并发访问的最大值
初始值为1时,代表同时只允许1条线程访问资源,保证线程的同步

调用otherTest会发现每隔两秒打印5条信息。

2.9 @synchronized

是对mutex的递归锁的封装,源码objc4中的objc-sync.mm文件查看
@synchronized(obj)内部会生成obj对应的递归锁,然后进行加锁解锁操作

本文来自: 旺仔大包子

推荐上篇文章: iOS多线程之NSOperation的使用

Java多线程编程——对象及变量的并发访问

Java多线程编程——对象及变量的并发访问


文章目录


前言

本章主要介绍Java多线程中的同步,也就是如何在Java语言中写出线程安全的程序,解决非线程安全的相关问题。在多线程中解决同步问题,是学习多线程的重中之重。
本章中应着重掌握如下技术点:

  • synchronized 对象监视器为Object 时的使用方法
  • synchronized 对象监视器为Class 时的使用方法
  • 非线程安全问题是如何出现的
  • 关键字 volatile 的主要作用
  • 关键字 volatile 与 synchronized 的区别及使用情况

一、synchronized 同步方法

关键字 synchronized 可用来保障原子性、可见性和有序性。
非线程安全问题会在多个线程对同一个对象中的实例变量进行并发访问时发生,产生的后果就是“脏读”。也就是读取到的数据其实是被更改过的。而线程安全是指获得的实际变量的值是经过同步处理的,不会出现脏读的现象。

1. 方法内的变量为线程安全

非线程安全的问题存在于实际变量中,对于方法内部的私有变量,则不存在非线程安全问题。方法中的变量不存在非线程安全问题,永远都是安全的,这是因为方法内部的变量具有私有特性。

2. 实例变量非线程安全问题与解决方案

用线程访问的对象中如果有多个实例变量,则运行的结果有可能出现交叉的情况。如果对象仅有一个实例变量,则有可能出现覆盖的情况。
如果两个线程同时访问同一个业务对象中的一个没有同步的方法,如果两个线程同时操作业务对象中的实例变量,则有可能出现非线程安全问题,只需要在方法前加上关键字 synchronized 即可。
总结:两个线程同时访问同一个对象中的同步方法时一定是线程安全的。由于线程是同步访问的,不管哪个线程先运行,这个线程进入用 synchronized 声明的方法时就上锁,方法执行完成后自动解锁,之后下一个线程才会进入用 synchronized 声明的方法里,不解锁其他线程就执行不了该方法。

3. 同步 synchronized 在字节码指令中的原理

在方法中使用 synchronized 关键字实现同步的原因是使用了 flag 标记 ACC_SYNCHRONIZED,调用方法时,调用指令会检查方法的 ACC_SYNCHRONIZED 访问标志是否设置,如果设置了,执行线程先持有同步锁,然后执行方法,最后在方法完成时释放锁。
在cmd中使用 javap 命令将 class 文件转成字节码指令,参数 -v 用于输出附加信息,参数 -c 用于对代码进行反汇编。javap.exe 命令如下:
javap -c -v Test.class
同步:按顺序执行A和B这两个业务
异步:执行A业务时,B业务也在同时执行

4. 多个对象多个锁

若存在多个业务对象,线程和业务对象属于一对一的关系,每个线程执行自己所属业务对象中的同步方法,不存在争抢关系,所以运行结果是异步的。另外 synchronized 可以不需要,因为不会出现非线程安全的问题。
只有在多个线程执行相同业务对象中的同步方法时,线程和业务对象属于多对一的关系,为了避免出现非线程安全问题,所以使用 synchronized。

5. 将 synchronized 方法与对象作为锁


总结

提示:这里对文章进行总结:
例如:以上就是今天要讲的内容,本文仅仅简单介绍了pandas的使用,而pandas提供了大量能使我们快速便捷地处理数据的函数和方法。

以上是关于多线程安全问题及各种锁的主要内容,如果未能解决你的问题,请参考以下文章

Java各种锁机制简述

Java线程 — 线程同步及安全问题

多线程学习 各种锁类型与对比 lock

.NET面试题系列数据结构(ArrayListQueueStack)及线程安全问题

Linux多线程_(线程池,单例模式,读者写者问题,自旋锁)

iOS线程同步(各种锁)