欢迎访问 生活随笔!

尊龙游戏旗舰厅官网

当前位置: 尊龙游戏旗舰厅官网 > > 编程问答 >内容正文

编程问答

java多线程基本概述(二十六)——免锁容器 -尊龙游戏旗舰厅官网

发布时间:2025/1/21 编程问答 24 豆豆
尊龙游戏旗舰厅官网 收集整理的这篇文章主要介绍了 java多线程基本概述(二十六)——免锁容器 小编觉得挺不错的,现在分享给大家,帮大家做个参考.

容器是所有编程中的基础工具,这其中也包括并发编程。出于这个原因,像vector和hashtable这类早期容器具有许多synchronized方法,当他们用于非多线程的应用程序中时,便会导致不可接受的开销。在java1.2中,新的容器类库是不同步的,并且collections类提供了各种static的同步的装饰方法,从而来同步不同类型的容器。尽管这是一种改进,因为它使你可以选择在你的容器中是否要使用同步,但是这种开销仍旧是基于synchronized加锁机制的。java se5特别添加了新容器,通过使用更灵巧的技术消除加锁,从而提高线程的安全的性能。

这些免锁容器背后的通用策略是:对容器的修改可以与读取操作同时发生,只要读取者只能看到完成修改后的结果即可。修改是在容器数据结构的某一部分的一个单独的副本(有时是整个数据结构的副本)上执行的,并且这个副本在修改过程中是不可视的。只有当修改完成时,被修改的结构才会自动地与主数据结构进行交换,之后读取者就可以看到这个修改了。

在copyonwritearraylist中,写入将导致创建整个底层数组的副本,而源数组将保留在原地,使得复制的数组在被修改时,读取操作可以安全的执行。当修改完成时,一个原子性操作将把新的数组换入,使得新的读取操作可以看到这个新的修改。copyonwritearraylist的好处之一是当多个迭代器同时遍历和修改这个列表时,不会抛出concurrentmodificationexception,因此你不必编写特殊的代码去防范这种异常,就像你以前必须作的那样。

迭代器上进行的元素更改操作(remove、set 和 add)不受支持。这些方法将抛出 unsupportedoperationexception。

copyonwritearrayset将使用copyonwritearraylist来实现其免锁行为。

concurrenthashmap和concurrentlinkedqueue使用了类似的技术,允许并发的读取和写入,但是容器中只有部分内容而不是整个容器可以被复制和修改。然而,任何修改在完成之前,读取者仍旧不能看到它们。concurrenthashmap不会抛出concurrentmodificationexception异常。

  copyonwritearraylist适合用在“读多,写少”的“并发”应用中,换句话说,它适合使用在读操作远远大于写操作的场景里,比如缓存。它不存在“扩容”的概念,每次写操作(add or remove)都要copy一个副本,在副本的基础上修改后改变array引用,所以称为“copyonwrite”,因此在写操作是加锁,并且对整个list的copy操作时相当耗时的,过多的写操作不推荐使用该存储结构。

 

一个concurrenthashmap由多个segment组成,每一个segment都包含了一个hashentry数组的hashtable, 每一个segment包含了对自己的hashtable的操作,比如get,put,replace等操作,这些操作发生的时候,对自己的hashtable进行锁定。由于每一个segment写操作只锁定自己的hashtable,所以可能存在多个线程同时写的情况,性能无疑好于只有一个hashtable锁定的情况。

 

 

源码分析 在concurrenthashmap的remove,put操作还是比较简单的,都是将remove或者put操作交给key所对应的segment去做的,所以当几个操作不在同一个segment的时候就可以并发的进行。

public v remove(object key) {int hash = hash(key.hashcode());return segmentfor(hash).remove(key, hash, null);}

而segment中的remove操作除了加锁之外和hashmap中的remove操作基本无异

v remove(object key, int hash, object value) {lock();try {int c = count - 1;hashentry[] tab = table;int index = hash & (tab.length - 1);hashentry first = tab[index];hashentry e = first;while (e != null && (e.hash != hash || !key.equals(e.key)))e = e.next;v oldvalue = null;if (e != null) {v v = e.value;if (value == null || value.equals(v)) {oldvalue = v;// all entries following removed node can stay// in list, but all preceding ones need to be// cloned. modcount;hashentry newfirst = e.next;for (hashentry p = first; p != e; p = p.next)newfirst = new hashentry(p.key, p.hash,newfirst, p.value);tab[index] = newfirst;count = c; // write-volatile }}return oldvalue;} finally {unlock();}}

 

package tij;import java.util.arraylist; import java.util.list; import java.util.concurrent.executorservice; import java.util.concurrent.executors;/*** created by huaox on 2017/4/21.**/ public class copyonwritearraylistdemo {private static class readtask implements runnable {list list;readtask(list list) {this.list = list;}public void run() {for (string str : list) {system.out.println(str);}}}private static class writetask implements runnable {list list;int index;writetask(list list, int index) {this.list = list;this.index = index;}public void run() {list.remove(index);list.add(index, "write_" index);}}public void run() {final int num = 5;list list = new arraylist();for (int i = 0; i < num; i ) {list.add("main_" i);}executorservice executorservice = executors.newfixedthreadpool(num);for (int i = 0; i < num; i ) {executorservice.execute(new readtask(list));executorservice.execute(new writetask(list, i));}executorservice.shutdown();}public static void main(string[] args) {new copyonwritearraylistdemo().run();} }

输出结果:

exception in thread "pool-1-thread-3" java.util.concurrentmodificationexception main_0 main_1at java.util.arraylist$itr.checkforcomodification(arraylist.java:901) main_2 main_3at java.util.arraylist$itr.next(arraylist.java:851) main_4 main_0 main_1 write_2 main_3 main_4 main_0 main_1 write_2 write_3 main_4 main_0 write_0 write_1at tij.copyonwritearraylistdemo$readtask.run(copyonwritearraylistdemo.java:22) write_2 write_3at java.util.concurrent.threadpoolexecutor.runworker(threadpoolexecutor.java:1142) write_4at java.util.concurrent.threadpoolexecutor$worker.run(threadpoolexecutor.java:617)at java.lang.thread.run(thread.java:745)process finished with exit code 0

免锁容器的源码为赋值底层数组,然后赋新值,改引用

public boolean add(e e) {final reentrantlock lock = this.lock;lock.lock();try {object[] elements = getarray();int len = elements.length;object[] newelements = arrays.copyof(elements, len 1);newelements[len] = e;setarray(newelements);return true;} finally {lock.unlock();}}

修改为copyonwritearraylist后

public void run() {final int num = 5;list list = new copyonwritearraylist<>();for (int i = 0; i < num; i ) {list.add("main_" i);}executorservice executorservice = executors.newfixedthreadpool(num);for (int i = 0; i < num; i ) {executorservice.execute(new readtask(list));executorservice.execute(new writetask(list, i));}executorservice.shutdown();}

输出结果:

main_0 main_1 main_2 main_3 main_4 main_0 main_1 write_2 main_3 main_4 write_0 main_1 write_2 write_3 main_4 write_0 main_1 write_2 write_3 write_4 write_0 write_1 write_2 write_3 write_4process finished with exit code 0

 

转载于:https://www.cnblogs.com/soar-hu/p/6742694.html

总结

以上是尊龙游戏旗舰厅官网为你收集整理的java多线程基本概述(二十六)——免锁容器的全部内容,希望文章能够帮你解决所遇到的问题。

如果觉得尊龙游戏旗舰厅官网网站内容还不错,欢迎将尊龙游戏旗舰厅官网推荐给好友。

网站地图