线程安全容器

1.同步容器类

同步容器类包括Vector和Hashtable,或者通过Collections.synchronizedXxx等工长方法创建的。

这些实现线程安全的方式:将他们的状态封装起来,并对每个共有方法加锁。

1.1 同步容器类的问题

复合操作:迭代(反复访问元素,直到遍历完容器中所有元素)、条件运算(若没有则添加)。在其他线程并发地修改容器时,它们可能会变现出意料之外的行为。

如:

public static Object getLast(Vector list) {
    int lastIndex = list.size() - 1;
    return list.get(lastIndex);
}

public static void deleteLast(Vector list) {
    int lastIndex = list.size() - 1;
    list.remove(lastIndex);
}

当两个线程同时分别执行上面的两个方法时。会出现问题。如线程A调用getLast(), 线程B调用deleteLast(); 调用交替执行的图如下:
这里写图片描述

结构是:getLast将抛出ArrayIndexOutOfBoundsException异常。

1.2 客户端加锁实现同步容器问题

我们解决的思路是通过加锁来保证Vector的大小在调用size和get之间不会发生变化。
如:

public static Object getLast(Vector list) {
    synchronized(list) {
       int lastIndex = list.size() - 1;
       return list.get(lastIndex);
    }
}

public static void deleteLast(Vector list) {
    synchronized(list) {
        int lastIndex = list.size() - 1;
        list.remove(lastIndex);
    }
}

1.3 迭代器与ConcurrentModificationException

同步容器类的迭代器在迭代时,如果被修改时,会变现出“及时失败”的异常,即ConcurrentModificationException异常。

除了显示的调用迭代会变现出“及时失败” 的异常外。隐式的调用以下方法也有可能出现“及时失败”,如:hashCode 、equals、containsAll、removeAll和retainAll等方法。

这种“及时失败”的迭代器并不是一种完备的处理机制,而只是“善意地” 捕获并发错误,因此只能作为并发问题的预警指示器。

解决办法:

  1. 对容器加锁
  2. 克隆容器

2. 并发容器

并发容器是从Java 5.0 后引入的。主要的目的是:通过并发容器来代替同步容器,可以极大地提高伸缩性并降低风险。

猜你喜欢

转载自blog.csdn.net/ai_xiangjuan/article/details/80247470