博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【集合类型的并发】Collections.synchronizedList
阅读量:5807 次
发布时间:2019-06-18

本文共 4238 字,大约阅读时间需要 14 分钟。

摘要: 详细的解析:Collections.synchronizedList    1 :关注要点,为什么在有synchroniezed方法的同时会出现 Collections.synchronizedList    2 :知识背景: 您可能需要了解java Synchronized方法的加锁的各种机制,包括如何上锁,锁对象    3 : plus: 您需要不断的深化 Java加锁的各种机制@NotThreadSafe  class BadListHelper 
{ public List
list = Collections.synchronizedList(new ArrayList
()); public synchronized boolean putIfAbsent(E x) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } 这个示例希望实现的功能是为List提供一个原子操作:若没有则添加。因为ArrayList本身不是线程安全的,所以通过集合Collections.synchronizedList将其转换为一个线程安全的类,然后通过一个辅助的方法来为List实现这么个功能。初看起来这个方法没问题,因为也添加了synchronized关键字实现加锁了。 但是仔细分析,你会发现问题。首先对于synchronized关键字,需要说明的是,它是基于当前的对象来加锁的,上面的方法也可以这样写:public boolean putIfAbsent(E x) { synchronized(this) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } 所以这里的锁其实是BadListHelper对象, 而可以肯定的是Collections.synchronizedList返回的线程安全的List内部使用的锁绝对不是BadListHelper的对象,应为你在声明和初始化这个集合的过程之中,你尚且都不知道这个对象的存在。所以BadListHelper中的putIfAbsent方法和线程安全的List使用的不是同一个锁,因此上面的这个加了synchronized关键字的方法依然不能实现线程安全性。 下面给出书中的另一种正确的实现:@ThreadSafe class GoodListHelper
{ public List
list = Collections.synchronizedList(new ArrayList
()); public boolean putIfAbsent(E x) { synchronized (list) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } } } 如果你要分析这个实现是否正确,你需要搞清楚Collections.synchronizedList返回的线程安全的List内部使用的锁是哪个对象,所以你得看看Collections.synchronizedList这个方法的源码了。该方法源码如下: public static
List
synchronizedList(List
list) { return (list instanceof RandomAccess ? new SynchronizedRandomAccessList
(list) : new SynchronizedList
(list)); } 通过源码,我们还需要知道ArrayList是否实现了RandomAccess接口:public class ArrayList
extends AbstractList
implements List
, RandomAccess, Cloneable, java.io.Serializable 查看ArrayList的源码,可以看到它实现了RandomAccess,所以上面的synchronizedList放回的应该是SynchronizedRandomAccessList的实例。接下来看看SynchronizedRandomAccessList这个类的实现:static class SynchronizedRandomAccessList
extends SynchronizedList
implements RandomAccess { SynchronizedRandomAccessList(List
list) { super(list); } SynchronizedRandomAccessList(List
list, Object mutex) { super(list, mutex); } public List
subList(int fromIndex, int toIndex) { synchronized(mutex) { return new SynchronizedRandomAccessList
( list.subList(fromIndex, toIndex), mutex); } } static final long serialVersionUID = 1530674583602358482L; private Object writeReplace() { return new SynchronizedList
(list); } } 因为SynchronizedRandomAccessList这个类继承自SynchronizedList,而大部分方法都在SynchronizedList中实现了,所以源码中只包含了很少的方法,但是通过subList方法,我们可以看到这里使用的锁对象为mutex对象,而mutex是在SynchronizedCollection类中定义的,所以再看看SynchronizedCollection这个类中关于mutex的定义部分源码:Java代码 收藏代码static class SynchronizedCollection
implements Collection
, Serializable { private static final long serialVersionUID = 3053995032091335093L; final Collection
c; // Backing Collection final Object mutex; // Object on which to synchronize SynchronizedCollection(Collection
c) { if (c==null) throw new NullPointerException(); this.c = c; mutex = this; } SynchronizedCollection(Collection
c, Object mutex) { this.c = c; this.mutex = mutex; } } 可以看到mutex就是当前的SynchronizedCollection对象,而SynchronizedRandomAccessList继承自SynchronizedList,SynchronizedList又继承自SynchronizedCollection,所以SynchronizedRandomAccessList中的mutex也就是SynchronizedRandomAccessList的this对象。所以在GoodListHelper中使用的锁list对象,和SynchronizedRandomAccessList内部的锁是一致的,所以它可以实现线程安全性。
class BadListHelper 
{ public List
list = Collections.synchronizedList(new ArrayList
()); public synchronized boolean putIfAbsent(E x) { boolean absent = !list.contains(x); if (absent) list.add(x); return absent; } }

putIfAbsent方法和List并不是使用的同一个锁对象,List使用的锁对象并不是BadListHelper,而是list。假如A线程进入putIfAbsent方法,list这个锁并没有被获取(A线程获取的是 BadListHelper这个对象),所以其他线程还能够获得list锁对象来改变list对象。boolean absent = !list.contains(x);当线程到这串代码结束时,其他线程获得list锁对象,从而就能调用list的方法来改变list对象,这时候就可能导致!list.contains(x)改变,即域absent并不是A线程得到的布尔类型。所以这个类并不是线程安全的。

获得变量的锁就可以改变变量,没有获得变量锁的就不能改变,获得方法的锁就可以执行方法里面的语句。

转载地址:http://wnubx.baihongyu.com/

你可能感兴趣的文章
Android SDK 的下载代理
查看>>
Method Swizzling对Method的要求
查看>>
佛祖保佑,永不宕机
查看>>
四、配置开机自动启动Nginx + PHP【LNMP安装 】
查看>>
LNMP一键安装
查看>>
SQL Server数据库概述
查看>>
Linux 目录结构及内容详解
查看>>
startx命令--Linux命令应用大词典729个命令解读
查看>>
华为3026c交换机配置tftp备份命令
查看>>
Oracle命令导入dmp文件
查看>>
OCP读书笔记(24) - 题库(ExamD)
查看>>
Http、TCP/IP协议与Socket之间的区别(转载)
查看>>
解决Unable to load R3 module ...VBoxDD.dll (VBoxDD):GetLastError=1790
查看>>
.net excel利用NPOI导入oracle
查看>>
vrpie在Visio Studio 中无法调试的问题
查看>>
第六课:数据库的基本工具
查看>>
关于二叉树重构的思索
查看>>
$_SERVER['SCRIPT_FLENAME']与__FILE__
查看>>
skynet实践(8)-接入websocket
查看>>
系统版本判断
查看>>