什么是线程安全?

如何理解

记得面试的时候被问到过这个问题,当时自己答得中规中矩,不怎么好。
“线程安全”不是指线程的安全,是指内存的安全。为什么如此说呢?这和操作系统有关。
目前主流操作系统都多个进程同时运行。为了保证安全,每个进程只能访问分配给自己的内存空间,而不能访问别的进程的,这是由操作系统保障的。在每个进程的内存空间中都会有一块特殊的公共区域,通常称为堆(内存)。进程内的所有线程都可以访问到该区域,这就是造成问题的潜在原因。所以线程安全指的是,在堆内存中的数据由于可以被任何线程访问到,在没有限制的情况下存在被意外修改的风险。

如何处理(理想化)

  1. 操作系统会为每个线程分配属于它自己的内存空间,通常称为栈内存,其它线程无权访问,这也是由操作系统保障的。所以如果想数据只被某个线程自己使用,可以将数据放在栈内存。常用的是局部变量基于位置唯一。
  2. 如何使资源在公共区域但又能保证安全呢?可以讲资源给每个需要的线程都拷贝一份。可以使用ThreadLocal类。
  3. 如果资源是只读变量或者常量,那即便在公共区域对于多线程来说也是安全的。

如何处理

  1. 使用互斥锁。第一个到的线程添加锁,没有用完我不释放锁,别的线程过来看到锁被别人拿着,只能选择等待或者去做别的事。
  2. CAS(Compare And Swap);之所以会出现CAS,因为锁的获取和释放是要花费一定代价的,如果在线程数目特别少的时候,可能根本就不会有别的线程来操作数据,此时你还要获取锁和释放锁,可以说是一种浪费。针对这种“地广人稀”的情况,专门提出了一种方法,叫CAS(Compare And Swap)。就是在并发很小的情况下,数据被意外修改的概率很低,但是又存在这种可能性,此时就用CAS。

所以CAS这种方式适用于并发量不高的情况,也就是数据被意外修改的可能性较小的情况(即使可能性较小,也有可能被改变,即ABA问题)。如果并发量很高的话,数据一定会被修改,每次都要放弃,然后从头再来,这样反而花费的代价更大了,还不如直接加锁呢。

解释下ABA问题,假如你睡觉前数据是5,醒来后数据还是5,并不能肯定数据没有被修改过。可能数据先被修改成8然后又改回到5,只是你不知道罢了。对于这个问题,其实也很好解决,再加一个版本号字段就行了,并规定只要修改数据,必须使版本号加1。

eg:你离开前数据是7版本号是0,醒来后数据是7版本号是0,表明数据没有被修改。如果数据是7版本号是3,表明数据被改动了3次,先改为其它,然后又改回到7。

你可能感兴趣的:(锁,线程安全,锁)