欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

java中的线程安全(Threadsafe)及其四种策略

程序员文章站 2022-07-14 10:40:55
...
  1. 线程安全:
线程之间存在“竞争条件”,作用于同一个mutable数据上的多个线程,彼此之间存在对该数据的访问竞争并导致interleaving,导致post-condition可能被违反,这是不安全的。

线程安全:ADT或方法在多线程中要执行正确。

要做到:不违反spec,保持RI。与多少处理器,如何调度线程无关,不需要在spec中强制要求client满足某种“线程安全”的义务。

例如:Iterator就不是线程安全的,因为你不能在迭代遍历的时候改变collection。

保证线程安全,有四种策略:

(1)限制数据共享(Confinement)

(2)共享不可变数据(Immutability)

(3)共享线程安全的可变数据(Threadsafe data type)

(4)同步机制(Synchronization)

下面依次进行介绍:

  1. Confinement-限制数据共享
    

将可变数据限制在单一线程内部,避免竞争。

不允许任何线程直接读写该数据。

核心思想: 线程之间不共享mutable数据类型

避免全局变量:例如我们之前的单例模式(Singleton Design Pattern)就存在风险:

  1. Immutability:
    

    使用不可变数据类型和不可变引用,避免多线程之间的race condition。

    关于不可变的更强定义:

    (1)无mutator方法

    (2)所有属性均为private和final的

    (3)没有表示泄露

    (4)表示中没有对可变类型的变化,甚至有益的变化也不允许

    不允许子类重写方法:最简单的做法实在类前加上关键final

  2.  Using Threadsafe Data Types:
    
    如果必须要用mutable的数据类型在多线程之间共享数据,要使用线程安全的数据类型。 
    
     在JDK中的类,文档中明确指明了是否threadsafe
    
     一般来说, JDK同时提供两个相同功能的类,一个是threadsafe,另一个不是。 原因:threadsafe的类一般性能上受影响 。
    
     集合类都是线程不安全的。
    
     因此Java API提供了进一步的decorator:
    
     
    
     进行包装(Wrap)
    

    要注意的是:

    在使用synchronizedMap(hashMap)之后,不要再把参数hashMap共享给其他线程,不要保留别名,一定要彻底销毁。

    即使在线程安全的集合类上,使用iterator也是不安全的,除非使用lock机制(后续介绍)。

  3. Locks and Synchronization:

同步与锁,最复杂也最具有价值的threadsafe策略。

前三种策略的核心思想:

避免共享->即使共享,也只能读/不可写(immutable)->即使可写,共享的可写数据

也应自己具备在多线程之间协调的能力,即使用线程安全的mutable ADT。

而很多时候,无法满足上述三个条件。

因此采用同步与锁策略:

 程序员来负责多线程之间对mutable数据的共享操作,通过“同步” 策略,避免多线程同时访问数据。

一个锁就是一个线程在一个时间段对数据的占用。使用锁机制,获得对数据的独家mutation权,其他线程被阻塞,不得访问。

lock的两个操作:

获得与释放。

可以使用synchronized方法,或在方法中加入代码块:

后者需要显式的给出lock,且不一定非要是this,能提供更加精细的lock粒度。

任何共享的mutable变量/对象必须被lock保护

涉及到多个mutable变量的时候,必须被同一个lock所保护。

同步机制给性能带来很大影响,除非必要,否则不要用。Java中很多mutable 的类型都不是threadsafe就是这个原因。

尽可能减小lock的范围。

线程安全的声明:

Concurrency argument:

threadsafe by monitor pattern:all accesses to rep are guarded by this object's lock。
相关标签: 线程安全

上一篇: throws与throw

下一篇: throw 与throws