Java并发编程 同步容器


本文整理自《Java并发编程实战》一书。


 

同步容器类包括Vector和Hashtable,二者都是早期JDK的一部分,此外还包括在JDK1.2中添加的一些功能相似的类,这些同步的封装器类有Collections.synchronizedXxx等工厂方法创建的。这些类实现线程安全的方式是:将它们的状态封装起来,并对每个公共方法都进行同步,使得每次都只有一个线程能访问容器的状态。

 

同步容器都是线程安全的,但在某些情况下可能需要额外的客户端加锁来保护复合操作。容器上常见的复合操作包括:迭代遍历、跳转(根据指定顺序找到当前元素的下一个元素)以及条件运算,例如“putIfAbsent”。在同步容器类中,这些复合操作在没有客户端加锁的情况下仍然是线程安全的,但当其他线程并发地修改容器时,它们可能会表现出意料之外的行为。

 

如Vector中定义两个方法:getLast 和 deleteLast,它们都会执行“先检查后运行”操作。

public static Object getLast(Vector list){
       intlastIndex = list.size() – 1;
       returnlist.get(lastIndex);
}
public static void deleteLast(Vector list){
       intlastIndex = list.size() – 1;
       list.remove(lastIndex);
}

这些方法看似没有任何问题,从某种程度来看也确实如此----无论多少个线程同时调用它们,也不会破坏Vector,但是从这些方法的调用角度来看,情况就不同了。如果线程A在包含10个元素的Vector上调用getLast,同时线程B在同一个Vector上调用deleteLast,则getLast将抛出ArrayIndexOutOfBoundsException异常。虽然这种情况很好地遵循了Vector的规范----如果请求一个不存在的元素则抛出一个异常,但这并不是getLast的调用者所希望得到的结果。

 

由于同步容器类要遵循同步策略,即支持客户端加锁。因此可以通过同步容器类的自身锁来保护它的每个方法,进而是getLast和deleteLast成为原子操作

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);
}
}

在调用size和相应的get之间,Vector的长度可能会发生变化,如:

for ( int i=0 ; i< vector.size() ; i++ ){
       doSomething(vector.get(i));
}

这种迭代在单线程环境下完全正确,但是在有其他线程并发修改Vector时,则可能导致麻烦。与getLast一样,如果对Vector进行迭代时,另一个线程删除了一个元素,并且这两个操作交替执行,那么这种迭代也会抛出ArrayIndexOutOfBoundsException。虽然这种迭代可能会抛出异常,但这并不意味着Vector就不是线程安全的。Vector的状态仍然是有效的,而抛出的异常也与其规范保持一致。然而,像在抛出异常显然不是人们期望的,也即意料之外的。

 

于是,我们可以通过客户端加锁来解决不可靠迭代的问题,但是要牺牲一些伸缩性,如下代码可以修复上述问题,但是会降低并发性:

synchronized(vector){
       for ( int i=0 ; i<vector.size() ; i++ ){
              doSomething(vector.get(i));
       }
}

 Sun工程师在设计同步容器类的迭代器时并没有考虑并发修改问题,并且它们的表现出的行为是“及时失败 fail-fast”的。这意味着,当它们发现容器在迭代过程中被修改时,就会抛出一个ConcurrentModificationException异常。虽然加锁可以防止迭代器抛出ConcurrentModificationException,但是你必须要记住在所有对共享容器进行迭代的地方都需要加锁。请注意,隐藏迭代器问题,如容器的 toString()、hashCode()、equals()、containsAll()、removeAll()、retainAll()等方法,都会对容器进行迭代。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!
提供的源码资源涵盖了小程序应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值