java中concurrent包并发包什么用

温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'浅谈java.util.concurrent包的并发处理(4)',
blogAbstract:'concurrent并发包里面几个重要的接口有:Executor、ExecutorService, ScheduledExecutorService;重要的实现类有:',
blogTag:'',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:0,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
hmcon:'0',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}让天下没有难学的技术
为什么java.util.concurrent 包里没有并发的ArrayList实现?
为什么java.util.concurrent 包里没有并发的ArrayList实现?
作者:Stephen C 译者:郑旭东
校对:方腾飞
问:JDK 5在java.util.concurrent里引入了ConcurrentHashMap,在需要支持高并发的场景,我们可以使用它代替HashMap。但是为什么没有ArrayList的并发实现呢?难道在多线程场景下我们只有Vector这一种线程安全的数组实现可以选择么?为什么在java.util.concurrent 没有一个类可以代替Vector呢?
答:我认为在java.util.concurrent包中没有加入并发的ArrayList实现的主要原因是:很难去开发一个通用并且没有并发瓶颈的线程安全的List。
像ConcurrentHashMap这样的类的真正价值(The real point / value of classes)并不是它们保证了线程安全。而在于它们在保证线程安全的同时不存在并发瓶颈。举个例子,ConcurrentHashMap采用了锁分段技术和弱一致性的Map迭代器去规避并发瓶颈。
所以问题在于,像“Array List”这样的数据结构,你不知道如何去规避并发的瓶颈。拿contains() 这样一个操作来说,当你进行搜索的时候如何避免锁住整个list?
另一方面,Queue 和Deque (基于Linked List)有并发的实现是因为他们的接口相比List的接口有更多的限制,这些限制使得实现并发成为可能。
CopyOnWriteArrayList是一个有趣的例子,它规避了只读操作(如get/contains)并发的瓶颈,但是它为了做到这点,在修改操作中做了很多工作和修改可见性规则。 此外,修改操作还会锁住整个List,因此这也是一个并发瓶颈。所以从理论上来说,CopyOnWriteArrayList并不算是一个通用的并发List。
原创文章,转载请注明: 转载自本文链接地址:
在华为努力练级的程序员,喜欢编程和琢磨新玩意
Latest posts by 郑 旭东 ()
Related posts:
(2 votes, average: 5.00 out of 5)
Loading...Java线程(十四):Concurrent包中强大的并发集合类
& 我们平时写程序需要经常用到集合类,比如ArrayList、HashMap等,但是这些集合不能够实现并发运行机制,这样在服务器上运行时就会非常的消耗资源和浪费时间,并且对这些集合进行迭代的过程中不能进行操作,否则会出现错误,例如下面程序:
public class CollectionModifyExceptionTest {&
&&& public static void main(String[] args) {&
&&&&&&& Collection users = new ArrayList();&
&&&&&&& users.add(new User(&张三&,28));&&&&
&&&&&&& users.add(new User(&李四&,25));&&&&&&&&&&&&
&&&&&&& users.add(new User(&王五&,31));&&&&
&&&&&&& Iterator itrUsers = users.iterator();&
&&&&&&& while(itrUsers.hasNext()){&
&&&&&&&&&&& System.out.println(&正在执行!&);&
&&&&&&&&&&& User user = (User)itrUsers.next();&
&&&&&&&&&&& if(&李四&.equals(user.getName())){&
&&&&&&&&&&&&&&& users.remove(user);&
&&&&&&&&&&& } else {&
&&&&&&&&&&&&&&& System.out.println(user);&&&&&&&&&&&&&&&&
&&&&&&&&&&& }&
&&&&&&& }&
&&&&&& 程序在执行过程中会报错, 因为定义了一个ArrayList类型的集合,但是在对集合进行迭代的时候又出现了users.remove(user),即从集合从删除数据,对于普通的集合来说这是不允许的,Java 5以后出现的并发集合类就是专门针对普通集合出现不能并发和不能在迭代过程中修改数据等问题而出现的。
&并发集合类主要有:
ConcurrentHashM&&&&&&&&& ConcurrentSkipListM&&&&&&&&&&&&& ConCurrentSkipListS&&&&&&&&&& CopyOnWriteArrayL CopyOnWriteArrayS&&&&&&&& ConcurrentLinkedQ
下面其中的某些集合类进行分别简述:
&ConcurrentHashMap支持完全并发的检索和更新所希望的可调整并发的哈希表。此类遵守与 Hashtable 相同的功能规范,并且包括对应于 Hashtable 的每个方法的方法版本。不过,尽管所有操作都是线程安全的,但检索操作不 必锁定,并且不 支持以某种防止所有访问的方式锁定整个表。此类可以通过程序完全与 Hashtable 进行互操作,这取决于其线程安全,而与其同步细节无关。
主要构造方法有:
ConcurrentHashMap();& //创建一个带有默认初始容量、加载因子和 concurrencyLevel 的新的空映射。&
ConcurrentHashMap(int initialCapacity);&& //创建一个带有指定初始容量、默认加载因子和 concurrencyLevel 的新的空映射。&
ConcurrentHashMap(int initialCapacity, float loadFactor, int concurrencyLevel);& //创建一个带有指定初始容量、加载因子和并发级别的新的空映射。&
ConcurrentHashMap(Map&& t);&&& //构造一个与给定映射具有相同映射关系的新映射。&
ConcurrentLinkedQueue是一个基于链接节点的、无界的、线程安全的队列。此队列按照 FIFO(先进先出)原则对元素进行排序,队列的头部 是队列中时间最长的元素。队列的尾部 是队列中时间最短的元素。新的元素插入到队列的尾部,队列检索操作从队列头部获得元素。当许多线程共享访问一个公共 collection 时,ConcurrentLinkedQueue 是一个恰当的选择,此队列不允许 null 元素。
主要构造方法有:
ConcurrentLinkedQueue();& //创建一个最初为空的 ConcurrentLinkedQueue。&
ConcurrentLinkedQueue(Collection&& c);// 创建一个最初包含给定 collection 的元素的 ConcurrentLinkedQueue,按照此 collection 迭代器的遍历顺序来添加元素&
CopyOnWriteArrayList是ArrayList 的一个线程安全的变形,其中所有可变操作(添加、设置,等等)都是通过对基础数组进行一次新的复制来实现的。这一般需要很大的开销,但是当遍历操作的数量大大超过可变操作的数量时,这种方法可能比其他替代方法更 有效。在不能或不想进行同步遍历,但又需要从并发线程中排除冲突时,它也很有用。&快照&风格的迭代器方法在创建迭代器时使用了对数组状态的引用。此数组在迭代器的生存期内绝不会更改,因此不可能发生冲突,并且迭代器保证不会抛出 ConcurrentModificationException。自创建迭代器以后,迭代器就不会反映列表的添加、移除或者更改。不支持迭代器上更改元素的操作(移除、设置和添加)。这些方法将抛出 UnsupportedOperationException。
主要构造方法有:
CopyOnWriteArrayList();& // 创建一个空列表。&
CopyOnWriteArrayList(Collection&& c); //按照 Collection 的迭代器返回元素的顺序,创建一个包含指定 Collection 的元素的列表。&
CopyOnWriteArrayList(E[] toCopyIn);&& //创建一个新的 CopyOnWriteArrayList,它保持给定数组的副本。&
CopyOnWriteArraySet是对其所有操作使用 CopyOnWriteArrayList 的 Set。因此,它共享以下相同的基本属性:
它最适合于 set 大小通常保持很小、只读操作远多于可变操作以及需要在遍历期间防止线程间冲突的应用程序。
它是线程安全的。
因为通常需要复制整个基础数组,所以可变操作(添加、设置、移除,等等)的开销巨大。
迭代器不支持可变移除操作。
使用迭代器进行遍历的速度很快,并且不会与其他线程发生冲突。在构造迭代器时,迭代器依赖于不变的数组快照。
主要构造方法包括:
CopyOnWriteArraySet();&& //创建一个空 set&
CopyOnWriteArraySet(Collection&& c);&& //创建一个包含指定 Collection 中所有元素的 set&
&&&&&&& 讲完并发集合类以后再回到本文刚开始的例子中,如果将Collection users = new ArrayList();改写成Collection users = new CopyOnWriteArrayList();再运行程序,程序就会正常进行了,结果如下:
正在执行!&
{name:'张三',age:28}&
正在执行!&
正在执行!&
{name:'王五',age:31}&
(window.slotbydup=window.slotbydup || []).push({
id: '2467140',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467141',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467143',
container: s,
size: '1000,90',
display: 'inlay-fix'
(window.slotbydup=window.slotbydup || []).push({
id: '2467148',
container: s,
size: '1000,90',
display: 'inlay-fix'Java基础(9)
自JAVA1.5版本开始,引入了一个新的包:concurrent
他里面包含了大量牛逼且好用的工具,以后,所有你希望用死循环等手段做的事情、多线程要处理的事情,等等,首先要想到使用它。
1 发布/订阅(也就是队列)
如果我们希望有一个数据缓冲区,有人可以随意向里面写数据,有人可以从里面按照“先进来先出去”的原则得到数据,该怎么做?
最简单的做法就是设计一个尽人皆知的单例类,里面放一个数组,然后提供读和写的函数,并且,代码中要仔细考虑如何对这个数组进行同步互斥,如何控制这个数组的大小,如何保证读/写的性能。
现在,看看concurrent里面提供了什么牛逼的东西。
- 【LinkedBlockingQueue】类(无边界队列)
使用它,程序瞬间变的及其简单了:
final BlockingQueue&Integer& queue=new LinkedBlockingQueue&Integer&();
queue.put(i);
queue.take();
为了上面的需求,代码变得如此简单了,再不需要考虑什么所谓的互斥、同步、读写竞争、单例模式,等等。
而且,如果你在new对象的时候,传入一个数字,这就是变成有边界的队列了,比如:new LinkedBlockingQueue(10); 这样,在执行queue.put时,如果队列已经存了10个数据,那么程序就自然被阻塞起来了,直到有另外的线程从里面取了数据(执行了take),你根本不要写大量的代码来处理队列阻塞的问题了。
【DelayQueue】类(延迟队列)
如果,我们希望存入队列中的数据,在延迟期满后才能被取出来:比如,我们向缓存添加内容时,给每一个key设定过期时间,系统自动将超过过期时间的key清除。用这个类,就瞬间满足要求了。
DelayQueue 对元素进行持有,直到一个特定的延迟到期。
注入其中的元素必须实现 java.util.concurrent.Delayed 接口。
DelayQueue 将会在每个元素的 getDelay() 方法返回的值的时间段之后才释放掉该元素。如果返回的是 0 或者负值,延迟将被认为过期,该元素将会在 DelayQueue 的下一次 take
被调用的时候被释放掉。
还有其他有特色的工具类
比如:SynchronousQueue,这个是很牛B的工具,可以好好用用。
2 等待线程结束再做其他事情
在你的程序中,如果需要启动100个线程去做其他事情,但是,你又希望你的主程序在这100个线程都结束后再继续执行,该怎么做?
一种最土鳖的方式就是在线程代码的最后一行,把一个状态记录到数据库中,然后你的主程序死循环去读数据库。
更优雅的做法是使用join来解决这个问题。
为了理解join的用法,我们假定有下面一个场景:
有三个工人,分别叫Worker_1,Worker_2,Worker_3。有一个任务需要他们三个人协作完成,且,Worker_3开始工作的前提是Worker_1和Worker_2完工后再开始,而Worker_1和Worker_2是并行同时工作。
为此,我们的程序可以这样写:
创建1个继承自Thread的类:Worker
在我们的主函数中,以如下的代码来实现上面的需要:
Worker Worker_1 = new Worker(“工人1”);
Worker Worker_2 = new Worker(“工人2”);
Worker Worker_3 = new Worker(“工人3”);
Worker_1.start();
Worker_2.start();
Worker_1.join();
Worker_2.join();
Worker_3.start();
这样,Worker_3这个线程,就会等待1和2这两个线程结束后才会被启动。
理解了join后,看看concurrent里面提供了什么牛逼的东西来处理同样的问题。
【CountDownLatch】类
join能有的能力,他都实现了。
它有一个await()方法,调用后,就会一直阻塞。它的实现原理是通过其内部的一个计数器逐渐递减,在减为0之前,await()方法都会阻塞着:
CountDownLatch countDownLatch = new CountDownLatch(2);
Worker Worker_1 = new Worker(“工人1”, countDownLatch);
Worker Worker_2 = new Worker(“工人2”, countDownLatch);
Worker Worker_3 = new Worker(“工人3”, countDownLatch);
Worker_1.start();
Worker_2.start();
countDownLatch.await();
Worker_3.start();
使用起来,与join()的不同有3个:
要创建一个CountDownLatch对象,并传递给线程
在线程类中需要增加一行代码:countDownLatch.countDown(); 这行代码的作用就是让CountDownLatch对象的内部计数器减一。
代替join,使用await()方法实现在主程序中阻塞
看起来,使用CountDownLatch要比join麻烦,那么,为什么要用它?
为了理解CountDownLatch的用法,我们假定有下面一个场景:
有三个工人,分别叫Worker_1,Worker_2,Worker_3。有一个任务需要他们三个人协作完成,且,Worker_3开始工作的前提是Worker_1和Worker_2的工作完成了50%后再开始,而Worker_1和Worker_2是并行同时工作。
就这一个小小的不同,join就无法实现了,因为join只有一个能力,就是对调用了它的线程阻塞到执行结束。所以,如果我们要在被阻塞的线程内部的执行过程进行控制时,join就歇菜了,这时,就是CountDownLatch的用武之地了。
让程序睡眠一会
使用TimeUnit,代替原来大家经常使用的Thread.sleep。
使用CopyOnWriteArrayList。
在CopyOnWriteArrayList里处理写操作(包括add、remove、set等)是先将原始的数据通过Arrays.copyof()来生成一份新的数组。
然后在新的数据对象上进行写,写完后再将原来的引用指向到当前这个数据对象。这样保证了每次写都是在新的对象上(因为要保证写的一致性)。
然后读的时候就是在引用的当前对象上进行读。
CopyOnWriteArrayList中写操作需要大面积复制数组,所以性能肯定很差,但是读操作因为操作的对象和写操作不是同一个对象,读之间也不需要加锁。
在对一个ArrayList对象进行大量读,而仅有少量写的场景下,很合适用它,比如缓存。
假定有如下一个场景:
在你的应用中有一个列表(List),它被频繁的遍历,但是很少被修改。就像“网站主页上的前十个分类”,它被频繁的访问,但是每个小时通过Quartz的Job来调度更新。如果使用ArrayList来作为该列表的数据结构并且不使用同步(synchronization),可能会遇到ConcurrentModificationException,因为在使用Quartz的Job修改该列表时,其他的代码可能正在遍历该列表。
如果为了这个需求,使用synchronization来解决,就得不偿失了。因为这个List是被高并发的读取,仅仅因为偶尔需要对其进行更新就是使用synchronization这种机制,其资源竞争会导致性能的极度恶化。
那么现在,CopyOnWriteArrayList就是一个牛逼方案了。
CopyOnWriteArrayList的另一个使用案例是观察者设计模式。如果事件监听器由多个不同的线程添加和移除,那么使用CopyOnWriteArrayList将会使得正确性和简单性得以保证。
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:4249次
排名:千里之外
原创:23篇
(5)(4)(6)(2)(2)(2)(5)(3)java.util.concurrent - Java并发工具包
java.util.concurrent - Java并发工具包
Java 5添加了一个新的包到Java平台,java.util.concurrnet包。这个包包含有一系列能够让Java的并发编程变得更加简单轻松的类。
1.BlockingQueue 阻塞队列
一个线程将会持续生产新对象并将其插入到队列之中,直到队列达到它所能容纳的临界点。也就是说,它是有限的,如果该阻塞队列到达了其临界点,负责生产的线程将会在往里面插入新对象时发生阻塞。
负责消费的线程将会一直从该阻塞队列中拿出对象。如果消费线程尝试去从一个空的队列中提取对象的时候,这个消费线程将会处于阻塞中,直到一个生产线程把一个对象丢进队列。
BlockingQueue是个接口,你需要使用它的实现之一使用BlockingQueue.
(1)ArrayBlockingQueue,是一个有界的阻塞队列。
(2)DelayQueue,对元素进行持有直到一个特定的延迟到期,注入其中的元素必须实现 java.util.concurrnet.Delayed接口。
(3)LinkedBlockingQueue,可以选择一个上限。
(4)PriorityBlockingQueue,无界队列,所有插入到PriorityBlockingQueue的元素必须实现parable接口。
(5)SynchronousQueue,它的内部同时只能容纳单个元素。
2.BlockingDeque &阻塞双端队列
双端队列是一个你可以从任意一端插入或提取元素的队列。这也是一个接口,它的实现类有LinkedBlockingDeque。
3.ConcurrentMap &并发Map
这是一个接口,其实现类为ConcurrentHashMap
4.CountDownLatch &闭锁
它允许一个或多个线程等待一系列指定操作的完成。
CountDownLatch以一个给定的数量初始化。countDown()每被调用一次,这一数量就减一。通过调用await()方法之一,线程可以阻塞等待这一数量到达零。
5.CyclicBarrier
这是一个同步机制,它能够处理一些算法的线程的同步问题。换句话说它就是一个所有线程必须等待的一栅栏,直到所有的线程都到达这里,然后所有线程才可以继续做其他事情。
6.Exchanger交换机
表示一种两个线程可以进行互相交换对象的会和点。
7.Semaphore信息量
(1)acquire()
(2)release()
信号量主要有两种用途:
(1)保护一个重要的(代码)部分防止一美人鱼超过N个线程进入。
(2)在两个线程之间发送信号
8.ExecutorService 执行器服务
类似于一个线程池。这是一个接口,它的实现类有ThreadPoolExecutor和ScheduledThreadPoolExecutor。
9.ForkJoinPool
在Java7中被引入,这是一个特殊的线程池,它的设计是为更好的配合分叉-合并任务的工作。
10.Lock 锁
它是一个类似于synchronized块的线程同步机制。但是Lock比synchronized块更加灵活、精细。
(1)synchroinzed代码块不能够保证进入访问等待的线程的先后顺序。
(2)不能够传递任何参数给一个synchronized代码块的入口
(3)synchronized块必须被完整地包含在单个方法里,而一个Lock对象的可以把它的lock和unlock方法的调用放在不同的方法里。
这是一个接口,它的实现类是ReetrantLock。
11.ReadWriteLock读定锁
它能够允许多个线程在同一时间对某特定资源进行读取,但同一时间内嗵有一个线程对其进行定入。这是一个接口,它的实现类是ReentrantReadWriteLock。
12.AtomicBoolean
AtomicInteger
AtomicLong
AtomicReference
我的热门文章
即使是一小步也想与你分享}

我要回帖

更多关于 concurrent包下的类 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信