如何理解什么是线程安全?
原文:http://netsecurity.51cto.com/art/201901/591186.htm
什么是线程安全
线程安全在多线程编程时是一个比较重要的概念,我们下先来看下维基百科是如何定义这个概念的:
https://en.wikipedia.org/wiki/Thread_safety
- Thread safety is a computer programming concept applicable to multi-threaded code. Thread-safe code only manipulates shared data structures in a manner that ensures that all threads behave properly and fulfill their design specifications without unintended interaction.
意思是说:
线程安全是应用于多线程代码的一种计算机编程概念,它确保多个线程能够按照程序的设计正确的访问共享数据结构。
或者再贴近编程语言的角度一点来讲,线程安全指的是同时最少有两个及以上的线程操作共享的数据区域,并且至少有一个是写操作。如果你还想不明白,可以去卫生间观察一下,一个厕位同时能有几个人使用。
线程安全的级别
线程安全的级别或者粒度有三种,如下:
(1)线程安全
这种情况下其实没有线程安全问题,比如上面的例子中,每个人都有自己专用的卫生间,所以不会存在竞争问题。
(2)条件安全
条件安全,顾名思义是有条件的,所有人共用几个卫生间,抢到资源的就把门关上,通过门来隔离资源,后面的人就在外面等待直到里面的人出来。
(3)不安全
这种情况下连门都没有,所以并不能很好保证资源安全,所以这种情况最好不能让同时让多个人直接使用。
实现线程安全的方式
大体来说有两种,首先我们明白安全问题来自于竞争,没有竞争就不会有问题。
方式一:
核心思路是避免共享数据结构,共享状态。包括:
(1)使用线程local变量
(2)使用不可变对象
方式二:
核心思路是共享不可避免,需要通过条件来确保按照。包括:
(1)互斥锁
(2)CAS原子操作
Java语言里面实现策略
这里以Java语言为例子,上面谈到的4种方式,其实在Java里面都支持,分别对应的解决手段为:
(1)ThreadLocal变量
(2)不可变对象有String,CopyOnWrite集合类
(3) 互斥锁包括JDK5之前的内置锁synchronized和JDK5之后的Lock接口
(4) J.U.C里面Atom开头的类
可以看出来Java里面的处理策略还是比较多的,当然不同的策略其实也有具体的适用场景,此外引入了线程安全和同步手段会对代码的性能造成一定的影响,这一点需要了解。
一般来说避免共享数据结构是能够比较优雅的解决并发问题,这种程序对多线程更友好,性能也会更高。比如单机的ThreadLocal和分布式的Ator模型。这里面不存在竞争。其次是不可变变量,多线程操作的都是CopyOnWrite,这也是为什么一些动态编程语言如Scala里面的默认数据结构大多数都是不可变的。不可变有不可变的好处,但缺点也是明显的,如果需要频繁对数据修改,那么会创建很多临时对象和占用更多的内存。
上面这两种场景,我们一般称为无锁实现,性能很好。如果避免不了共享数据,那么接着性能比较好的就是CAS这种原子操作,这种情况下我们一般也称是无锁的,但其实是利用了操作系统的原子指令来实现的,在竞争不激烈的场景下性能比较好,一般的编程语言都有封装好的工具类。如果竞争激烈,其实性能未必比使用互斥锁高。互斥锁一般也称重量级锁,需要OS干涉线程的调度,适合用于竞争激烈的场景下,这种方式下线程上下文的交换会降级系统的性能,在使用时需要注意。


浙公网安备 33010602011771号