线程安全

在这个多核的时代,代码的线程安全是应该优先被考虑的事情。

原子指令

其实不只是函数有线程安全的考量,在CPU的指令级别也有中断安全(有时候也表现为线程安全)的问题,所有就有了原子操作之说。

在一条涉及内存操作的指令执行期间,如果其使用的内存被其他线程修改,则会造成corrupt,因此CPU提供了LOCK指令,可以用于暂时锁定总线上的内存单元。

线程安全

线程安全是指函数在多个并发(Concurrence)或并行(Parallel)线程中异步执行时,可以被安全的调用,并且函数的行为是可以预期的,则说函数是线程安全的。

如果一个函数不依赖于共享资源,只使用传递给函数的参数和函数的本地变量,并且本地变量没有引用其他共享资源,函数内部没有调用其他非线程安全的函数,则这样的函数是线程安全的。如果线程在使用共享资源时正确的通过锁机制访问共享资源,那么也可以是线程安全的。

系统中每个线程有一个单独使用的栈,所有的调用参数和本地变量都在栈上分配,线程可以安全的使用栈上的变量,但是当栈上的变量引用其他共享资源时,则应做单独的考量。

可重入

如果函数可以被安全的异步并发或并行执行,函数的行为是可以预期的,并且函数自身不改变函数外部的任何状态,只通过输入参数和输出参数与外部通讯,则说函数是可重入的。也可以说是幂等的。

可重入的函数一定是线程安全的,但线程安全的函数不一定是可重入的,比如修改共享资源状态的线程安全函数是不可重入的。

有状态与无状态

这里只讨论(类)对象的有状态与无状态。

有数据成员的(类)对象是有状态的,不同的数据值代表了(类)对象不同的状态,而没有数据成员的(类)对象是无状态的,因为除了(类)对象的内存地址不同之外,无法通过其他方式区分出不同的对象。

如果有状态的对象,其数据成员不全部都是static静态的,那么这个对象不是线程安全的。因为不同的线程可能会同时修改这个对象的非static数据成员,造成不一致的情况出现。

如果是无状态的对象,或者是有状态的对象,但是其数据成员全部是static的,并且对象的成员函数都是线程安全的,那么这个对象也是线程安全的。

对于非线程安全的对象,可以通过将其私有状态存储到线程局部变量(java里面叫ThreadLocal)里面来使其成为线程安全的,当然其成员函数也应该是线程安全的。

在成员函数是线程安全的前提下,可以这样说,无状态的对象一定是线程安全的,有状态的对象不一定是线程不安全的,如果其状态都是静态的,那么有状态对象也是线程安全的。