zoukankan      html  css  js  c++  java
  • ArrayList, Vector和CopyOnWriteArrayList对比学习

    ArrayList线程不安全的例子

    线程安全就是多线程访问时,采用加锁机制,当一个线程访问该类的某个数据时,进行保护,其他线程不能进行访问直到该线程读取完,其他线程才可使用。不会出现数据不一致或者数据污染。线程不安全就是不提供数据访问保护,有可能出现多个线程先后更改数据造成所得到的数据时脏数据。List接口下有两个实现,一个是ArrayList,另一个是Vector。从源码角度来看,因为Vector的方法前加了synchronized关键字,也就是同步。ArrayList是高效的,Vector则是线程安全的。

    ArrayList不是线程安全的举个例子:一个ArrayList,在添加一个元素的时候,它有大体上有两步来完成:

    1. 在内部数组的size索引处存放此元素;
    2. 增大size的值

    在单线程情况下,如果size是0,添加一个元素之后,此元素在位置0,而且size=1;

    在多线程情况下,假设有两个线程,线程A现将元素放在0位置。但是此时CPU调度线程A暂停线程B得到运行机会,线程B也向ArrayList中添加元素,因为此时size仍然等于0(注意线程A只完成了添加的第一步,后面修该size的步骤还没做),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加size的值。对于这个ArrayList来说,元素实际上只有一个,存放在位置0,二size却等于2。

    附:ArrayList的add方法:

    1 public boolean add(E e) {
    2     // Increments modCount!!
    3     ensureCapacityInternal(size + 1);
    4     // 现在size处放置元素, 然后在增加size
    5     elementData[size++] = e;
    6     return true;
    7 }

    测试ArrayList线程不安全的demo:

     1 public class UnsafeArrayListDemo implements Runnable {
     2     private List<Integer> list = new ArrayList<Integer>();
     3 
     4     public void run() {
     5         try {
     6             Thread.sleep((int)Math.random());
     7         } catch (Exception e) {
     8             e.printStackTrace();
     9         }
    10         list.add(1);
    11     }
    12 
    13     public static void main(String[] args) throws Exception {
    14         ThreadGroup group = new ThreadGroup("group");
    15         UnsafeArrayListDemo unsafeArrayListDemo = new UnsafeArrayListDemo();
    16         for(int i=0; i<10000; i++) {
    17             Thread t = new Thread(group, unsafeArrayListDemo, String.valueOf(i));
    18             t.start();
    19         }
    20         // 等待线程组执行完毕
    21         while(group.activeCount() > 0) {
    22             Thread.sleep(100);
    23         }
    24         System.out.println(unsafeArrayListDemo.list.size());
    25     }
    26 }

    现在看下Vector中的add方法:

    1 public synchronized boolean add(E e) {
    2     modCount++;
    3     ensureCapacityHelper(elementCount + 1);
    4     elementData[elementCount++] = e;
    5     return true;
    6 }

    基本上和ArrayList一样,都是先添加,然后对size自增,唯一不同的是这个方法上加了关键字synchronized。可以在ArrayList的demo换成Vector来测试(本人已经测试过了,就不附代码了,直接替换ArrayList就好了)。Vector相对于ArrayList也会很暴力,为了线程安全就对方法加上了synchronized。

    Vector的方法在单个进行调用的时候虽然是线程安全的,但是进行方法的复合操作的时候仍然是线程不安全的,还需要客户端来进行加锁。

    Vector复合操作不安全的例子

     1 public class VectorDemo {
     2     // 获取最后一个元素
     3     public static Object getLast(Vector list) {
     4         int lastIndex = list.size() - 1;
     5         return list.get(lastIndex);
     6     }
     7     // 删除最后一个元素
     8     public static void deleteLast(Vector list) {
     9         int lastIndex = list.size() - 1;
    10         list.remove(lastIndex);
    11     }
    12 }

    像这种先获取值,然后进行操作都是不安全的,简单的方法就是在方法内部进行加锁。问题来了,怎么加锁呢?

  • 相关阅读:
    标签的讲解
    属性分类
    LeetCode 003. 无重复字符的最长子串 双指针
    Leetcode 136. 只出现一次的数字 异或性质
    Leetcode 231. 2的幂 数学
    LeetCode 21. 合并两个有序链表
    象棋博弈资源
    acwing 343. 排序 topsort floyd 传播闭包
    Leetcode 945 使数组唯一的最小增量 贪心
    Leetcode 785 判断二分图 BFS 二分染色
  • 原文地址:https://www.cnblogs.com/tuhooo/p/9333797.html
Copyright © 2011-2022 走看看