我有一个用例,其中多个线程可以读取和修改ArrayList,其中这些布尔值的默认值为True。
线程只能进行的修改是将ArrayList的元素从True设置为False。
所有线程也将同时读取ArrayList,但是可以读取陈旧的值。
注意: ArrayList的大小在ArrayList的整个生命周期中都不会改变。
是否有必要在这些线程之间同步ArrayList?我正在做的唯一同步是将ArrayList标记为volatile,以便对其进行的任何更新都将从线程的本地内存刷新回主内存。这样够了吗?
这是有关线程如何使用此ArrayList的示例代码
myList
是有问题的ArrayList,其值初始化为True
if (!myList.get(index)) {
return;
} else {
// do some operations to determine if we should
// update the value of myList to False.
if (needToUpdateList) {
myList.set(index, False);
}
}
我之前说过,这些线程并不关心陈旧的值,这是真的。但是,我还有另一个线程,该线程仅读取这些值并执行最后的操作。该线程确实关心陈旧性。同步要求会改变吗?
除了要求每次更新都同步之外,是否有更便宜的方法来“发布”更新的值?我正在尝试尽可能减少锁定。
答案 0 :(得分:2)
如in the Javadoc of ArrayList
所示:
请注意,此实现未同步。如果有多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。
您并不是在结构上修改列表,因此您不需要进行同步。
您要同步的另一个原因是避免读取过时的值。但你说那不在乎。
因此,没有理由进行同步。
如果您确实想读取过时的值,则需要进行同步。
同步的另一种避免锁定整个列表的方法是使其成为List<AtomicBoolean>
:这将不需要列表的同步,因为您不需要更改存储在列表中的值;但是读取AtomicBoolean
值可以保证可见性。
答案 1 :(得分:0)
这取决于元素为true时要执行的操作。考虑一下您的代码,其中一个单独的线程弄乱了您正在查看的值:
if (!myList.get(index)) { // <--- at this point, the value is True, so go to else branch
return;
} else {
// <--- at this point, other thread sets value to False.
// do some operations to determine if we should
// update the value of myList to False.
// **** Do these operations assume the value is still True?
// **** If so, then your application is not thread-safe.
if (needToUpdateList) {
myList.set(index, False);
}
}
答案 2 :(得分:0)
更新
我之前说过,这些线程并不关心陈旧的值,这是真的。但是,我有另一个线程仅读取这些值并执行一个最终操作。该线程确实关心陈旧性。同步要求会改变吗?
您只是使很多非常好的答案无效。
是的,同步现在很重要。实际上,原子性可能也很重要。使用同步列表,甚至可以使用并发列表或某种形式的地图。
每当您对列表进行读-修改-写操作时,您可能还必须持有同步锁以保留原子性:
synchronized( myList ) {
if (!myList.get(index)) {
return;
} else {
// do some operations to determine if we should
// update the value of myList to False.
if (needToUpdateList) {
myList.set(index, False);
}
}
}
编辑:要减少持有锁的时间,很大程度上取决于“执行某些操作”所花费的时间,但是CocurrentHashMap
可以减少锁争用,但要付出一些额外的开销。可能需要对代码进行性能分析以确定实际开销以及哪种方法更快/更好。
ConcurrentHashMap<Integer,Boolean> myList = new ConcurrentHashMap<>();
//...
if( myList.get( index ) != null ) return;
// "do some opertaions" here
if( needToUpdate )
myList.put( index, false );
但是我仍然不相信这不是过早的优化。首先编写正确的代码,完全同步列表可能没问题。然后分析工作代码。如果发现瓶颈,则可以担心减少锁争用是否是一个好主意。但是可能代码瓶颈不会出现在锁争用中,而实际上会完全不同。
答案 3 :(得分:0)
我做了更多的谷歌搜索,发现每个线程可能将值存储在注册表或本地缓存中。规范仅对何时将数据写入共享/全局内存提供某些保证。
https://docs.oracle.com/javase/specs/jls/se7/html/jls-17.html#jls-17.4.5
基本上是易失的,同步的,thread.start(),thread.join()...
是的,使用AtmoicBoolean可能是最简单的方法,但是您也可以同步或创建一个包含易失性布尔值的类。
检查此链接: http://tutorials.jenkov.com/java-concurrency/volatile.html#variable-visibility-problems