什么是线程安全(Thread Safety)?怎样才能做到线程安全?
线程安全
线程安全指某个函数、函数库在多线程环境中被调用时,能够正确地处理多个线程之间的共享变量,使程序功能正确完成。数据类型或静态方法在多个线程中使用时,无论线程如何执行,并且不需要调用代码进行额外的协调,都能够正确表现,则该方法是线程安全的。其中:
- 正确表现:指满足变量或方法的特定功能,并且能保持它的不变性。
- 无论线程如何执行:指多线程在多个处理器上执行或者以时间片轮转方式在单个处理器上执行
- 额外的协调:指数据类型不能在其调用者上设置与定时有关的前提条件,例如在 s e t ( ) set() set()进行的过程中不能调用 g e t ( ) get() get()
实现方法
-
线程封闭(Confinement)
共享可变数据是产生竞争的根源,通过限制可变数据的共享就可解决这个问题:
-
共享数据本地化
一个实现方法就是将共享变量本地化,每个线程都有该共享变量的私有副本。
-
避免使用全局变量
与本地变量不同,静态变量不会自动地被线程限制,如果程序里存在静态变量,就必须注明只有一个线程能使用它。所以最好能完全消除静态变量。
-
-
使用不可变数据(Immutability)
不可变数据是指一旦创建后状态不可改变的数据。这意味着共享只读数据,同时能获得线程安全性。然后可以通过这样的方式实现可变(非const)操作,即创建新对象而不是修改现有对象。这种方法是函数式编程的特征,同时还被Java、C#和Python中的字符串实现所使用。
-
使用线程安全的数据类型(Threadsafe data type)
使用线程安全的数据结构去存储共享可变数据,例如Java中的StringBuffer :该类型是线程安全的,在必要时会进行同步以使所有对它的操作能够以串行的方式进行,从而避免竞争。
-
同步(Synchronization)
该方法在无法避免共享可变数据的情况下使用。
-
互斥(Mutual exclusion)
对共享数据的访问使用进行序列化可确保在任何时间只有一个线程读取或写入共享数据的机制。由于不当使用会导致死锁、活锁和饥饿等问题,因此需要仔细考虑合并互斥的问题。
-
原子操作(Atomic operations)
通过原子操作可以访问共享数据,且该原子操作不能被其他线程中断。通常需要使用特殊的机器指令来实现,这些指令可能在运行时库(RTL)中提供。由于操作是原子操作,因此无论其他线程如何访问,共享数据始终保持有效状态。
-
参考链接: