首页 > 代码库 > 线程安全和线程不安全理解
线程安全和线程不安全理解
线程安全就是多线程訪问时,採用了加锁机制,当一个线程訪问该类的某个数据时,进行保护,其它线程不能进行訪问直到该线程读取完,其它线程才可使用。不会出现数据不一致或者数据污染。 线程不安全就是不提供数据訪问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据
=================================================================
概念:
假设你的代码所在的进程中有多个线程在同一时候执行,而这些线程可能会同一时候执行这段代码。假设每次执行结果和单线程执行的结果是一样的,并且其它的变量的值也和预期的是一样的,就是线程安全的。
或者说:一个类或者程序所提供的接口对于线程来说是原子操作或者多个线程之间的切换不会导致该接口的运行结果存在二义性,也就是说我们不用考虑同步的问题。
线程安全问题都是由全局变量及静态变量引起的。
若每一个线程中对全局变量、静态变量仅仅有读操作,而无写操作,一般来说,这个全局变量是线程安全的;若有多个线程同一时候运行写操作,一般都须要考虑线程同步,否则的话就可能影响线程安全。
安全性:
比方一个 ArrayList 类,在加入一个元素的时候,它可能会有两步来完毕:1. 在 Items[Size] 的位置存放此元素;2. 增大 Size 的值。
在单线程执行的情况下,假设 Size = 0,加入一个元素后,此元素在位置 0,并且 Size=1;
而如果是在多线程情况下,比方有两个线程,线程 A 先将元素存放在位置 0。可是此时 CPU 调度线程A暂停,线程 B 得到执行的机会。线程B也向此 ArrayList 加入元素,由于此时 Size 仍然等于 0 (注意哦,我们如果的是加入一个元素是要两个步骤哦,而线程A只完毕了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续执行,都添加 Size 的值。
那好,我们来看看 ArrayList 的情况,元素实际上仅仅有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。
安全性:
线程安全性不是一个非真即假的命题。 Vector 的方法都是同步的,而且 Vector 明白地设计为在多线程环境中工作。可是它的线程安全性是有限制的,即在某些方法之间有状态依赖(类似地,假设在迭代过程中 Vector 被其它线程改动,那么由 Vector.iterator() 返回的 iterator会抛出ConcurrentModifiicationException)。
对于 Java 类中常见的线程安全性级别,没有一种分类系统可被广泛接受,只是重要的是在编写类时尽量记录下它们的线程安全行为。
Bloch 给出了描写叙述五类线程安全性的分类方法:不可变、线程安全、有条件线程安全、线程兼容和线程对立。仅仅要明白地记录下线程安全特性,那么您是否使用这样的系统都没关系。这样的系统有其局限性 -- 各类之间的界线不是百分之百地明白,并且有些情况它没照应到 -- 可是这套系统是一个非常好的起点。这样的分类系统的核心是调用者能否够或者必须用外部同步包围操作(或者一系列操作)。以下几节分别描写叙述了线程安全性的这五种类别。
不可变
不可变的对象一定是线程安全的,而且永远也不须要额外的同步[1] 。由于一个不可变的对象仅仅要构建正确,其外部可见状态永远也不会改变,永远也不会看到它处于不一致的状态。Java 类库中大多数基本数值类如 Integer 、 String 和 BigInteger 都是不可变的。
须要注意的是,对于Integer,该类不提供add方法,加法是使用+来直接操作。而+操作是不具线程安全的。这是提供原子操作类AtomicInteger的原。
线程安全
线程安全的对象具有在上面“线程安全”一节中描写叙述的属性 -- 由类的规格说明所规定的约束在对象被多个线程訪问时仍然有效,无论执行时环境怎样排线程都不须要不论什么额外的同步。这样的线程安全性保证是非常严格的 -- 很多类,如 Hashtable 或者 Vector 都不能满足这样的严格的定义。
有条件的
有条件的线程安全类对于单独的操作能够是线程安全的,可是某些操作序列可能须要外部同步。条件线程安全的最常见的样例是遍历由 Hashtable 或者 Vector 或者返回的迭代器 -- 由这些类返回的 fail-fast 迭代器假定在迭代器进行遍历的时候底层集合不会有变化。为了保证其它线程不会在遍历的时候改变集合,进行迭代的线程应该确保它是独占性地訪问集合以实现遍历的完整性。通常,独占性的訪问是由对锁的同步保证的 -- 而且类的文档应该说明是哪个锁(一般是对象的内部监视器(intrinsic monitor))。
如果对一个有条件线程安全类进行记录,那么您应该不仅要记录它是有条件线程安全的,并且还要记录必须防止哪些操作序列的并发訪问。用户能够合理地如果其它操作序列不须要不论什么额外的同步。
线程兼容
线程兼容类不是线程安全的,可是能够通过正确使用同步而在并发环境中安全地使用。这可能意味着用一个 synchronized 块包围每个方法调用,或者创建一个包装器对象,当中每个方法都是同步的(就像 Collections.synchronizedList() 一样)。也可能意味着用 synchronized 块包围某些操作序列。为了最大程度地利用线程兼容类,假设全部调用都使用同一个块,那么就不应该要求调用者对该块同步。这样做会使线程兼容的对象作为变量实例包括在其它线程安全的对象中,从而能够利用其全部者对象的同步。
很多常见的类是线程兼容的,如集合类 ArrayList 和 HashMap 、 java.text.SimpleDateFormat 、或者 JDBC 类 Connection 和 ResultSet 。
线程对立
线程对立类是那些无论是否调用了外部同步都不能在并发使用时安全地呈现的类。线程对立非常少见,当类改动静态数据,而静态数据会影响在其它线程中运行的其它类的行为,这时一般会出现线程对立。线程对立类的一个样例是调用 System.setOut() 的类。
线程安全和线程不安全理解
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。