美文网首页
线程安全的意思

线程安全的意思

作者: mysimplebook | 来源:发表于2019-12-11 15:28 被阅读0次

    线程安全就是多线程访问时,采用了加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据是脏数据。

    我们都指定Java中集合类(Collection、Map)都未考虑线程安全,从源码的角度看一下ArrayList类为什么不是线程安全的类。如该类中在列表末尾插入元素的方法为

      /**

         * Appends the specified element to the endof this list.

         *

         * @param e element to be appended to thislist

         * @return true (asspecified by {@link Collection#add})

         */

        public boolean add(E e) {

            ensureCapacityInternal(size + 1);  // Increments modCount!!

            elementData[size++] = e;

            return true;

        }

    ArrayList的实现主要就是用了一个Object的数组,用来保存所有的元素,以及一个size变量用来保存当前数组中已经添加了多少元素。

    一个 ArrayList在末尾添加一个元素的时候,它可能会有两步来完成:

    首先,调用ensureCapacityInternal()这个方法,它的作用就是判断如果将当前的新元素加到列表后面,当前elementData数组的大小是否满足,如果size + 1的这个需求长度大于了elementData这个数组的长度,那么就要对这个数组进行扩容。

    其次,在elementData对应位置上设置值。

    在单线程运行的情况下,这个方法中的流程是一气呵成的。

    第二步中elementData[size++] = e操作不是一个原子操作,它由如下两步操作构成:

    elementData[size] = e;

    size = size + 1;

    如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度让线程A暂停(只完成了第一步),线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0,所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,线程A开始将size的值增加为1,线程B开始将size的值增加为2,都增加 Size 的值。现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2,下标1的位置上什么都没有。

    还有一种情况:线程A和B同时调用add方法,假设A执行add方法已经到了elementData[size] = e这一行,但此时调度器将A置回到Runnable状态,将B置于Running状态,而B执行add已完成了elementData[size++] = e,此时size刚好等于数组长度。若此时调度器将B置回到Runnable状态,将A置于Running状态,于是A开始执行elementData[size] = e,但size的长度等于数组长度,这必然造成ArrayIndexOutOfBoundsException。

    这就是“线程不安全”了。 线程存取同一对象相同资源(本例为size)时就会引发这种情况(称为竞速情况,race condition)。

    相关文章

      网友评论

          本文标题:线程安全的意思

          本文链接:https://www.haomeiwen.com/subject/ptoogctx.html