atomic 实际上相当于一个引用计数器 标记了atomic,那么被标记了的内存本身就有了一个引用计数器, 第一个占用这块内存的线程,会给这个计数器+1 在这个线程操作这块内存期间,其他线程在访问这个内存的时候,如果发现“引用计数器”不为0,则阻塞, 阻塞并不等于休眠,他是基于cpu轮询片, 每个cpu 轮询片到这个线程的时候都会尝试继续往下执行, 如果引用计数器为0,轮询片到来,则先给这块内存的引用计数器+1,然后再去操作,
char int long dobule 比如是这四种,如果在64位系统下,他们分别占1、4、8、8 个字节。 int 是绝对线程安全的,也就是说 系统对 char int 类型数据的操作,要么不操作,要么绝对会把这些字节全部操作完,不会并发的问题
long double 对一个long型数据的读写,操作系统有可能分两部分进行,一部分是高位,一部分是低位,所以两个线程同时操作long数据,有可能导致数据不同步 一个cpu的轮询片的周期,极大情况下都会比这个时间长 实际上即使不线程安全,两个线程也不会同时读写这块内存。
但是。一个线程读写内存仅仅只是一个方面,cpu 需要对数据进行计算, 这个计算的中间结果一般都会放到寄存器或者cpu 高速缓存, double 数据计算的复杂度远非long型所能比,因此double 数据类型相比long 型数据更容易出现并发的问题。
如果一个数据占的内存特别大,读写这块数据需要的时间也就越长,如果这个时间长度远远超过线程调度的轮询片,那么就有极大可能出现并发问题。
在64位的操作系统下,所有类型的指针,包括void * 都是占用8个字节的。超过4个字节的基本类型数据都会有线程并发的问题。
oc 下的 NSArray * 为例子,如果一个多线程操作这个数据,会有两个层级的并发问题: 指针本身 指针所指向的内存 如果没有atomic 修饰,并且假设现在有两个线程正在操作这个指针,一个是初始化线程,另一个线程就是读这个8字节的指针
首先,假如8字节内部存放的是0x1122334455667788 ok 8字节需要写入这个指,但与此同时,很不巧,另一个读线程现在要读这个8字节里面的值 假如 这个8字节只写了一半的时候 另一个线程来读,那它读到的可能是 0x1122334400000000 OK 实际上,等他读完之后,写线程仍然还未完成 [[NSArray alloc] init] 的头地址正确的应该是0x1122334455667788 ,而读线程读到的是0x1122334400000000 这时候会出现什么情况?野指针、野地址指向的是重要的一段数据
在0x1122334455667788 写完之前,读线程是无法读取的 在读线程正在读的过程中,写线程是无法改变8字节的 atomic 能避免这8字节的值因为多线程的原因被意外破坏
被atomic 修饰之后,你不可能随意去多线程操作这个8字节,但是对8字节里面所指向的n字节没有任何限制 atomic只能保证集合对象指针安全,集合里面的内容不保证。