首页 / JAVA / Java多线程:同步集合与同步锁
Java多线程:同步集合与同步锁
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了Java多线程:同步集合与同步锁,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3662字,纯文字阅读大概需要6分钟。
内容图文
Java多线程:同步集合与同步锁
同步集合
同步集合在多线程开发中扮演非常重要的角色,本文介绍些常用但被忽略的同步集合。
CopyOnWriteArrayList
Copy-On-Write是一种用于程序设计中的优化策略,基本思路是多个线程共享同一个列表,当某个线程想要修改这个列表的元素时会把列表中的元素Copy一份,然后进行修改,修改完后再讲新的元素设置给这个列表,是一种延时懒惰策略。好处是可以对CopyOnWrite容器进行并发的读,而不需要加锁,因为当前容器不会添加、移除任何元素。所以CopyOnWrite容器也是一种读写分离的思想,读和写不同的容器。使用Copy-On-Write机制实现的并发容器有两个分别是:CopyOnWriteArrayList和CopyOnWriteArraySet。
下面来分析下CopyOnWriteArrayList的核心源码,首先看下add方法:
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();
}
}
可以看到在添加的时候进行了加锁操作,否则多线程写的时候会Copy出N个副本出来。复制一份之后将新的元素设置到元素数组的len位置,然后再把最新的元素设置给该列表。
get方法:
public E get(int index) {
return get(getArray(), index);
}
读不需要加锁,如果读的时候多个线程正在向容器内添加数据,还是会读到旧数据,因为写的时候不会锁住旧的元素数组。
这种写时拷贝的原理优点是读写分离,并发场景下操作效率会提高,缺点是写操作时占用的内存空间翻了一倍,因此是以空间换时间。
ConcurrentHashMap
HashTable是HashMap的线程安全实现,但是HashTable使用synchronized来保证线程安全,这就会导致它的效率非常低下,因为当线程1使用put添加元素,线程2不但不能使用put添加元素,同时也不能使用get获取元素,竞争越激烈效率越低。
因此替代HashTable的ConcurrentHashMap就出现了,ConcurrentHashMap的优点在于容器里有多把锁,每一把锁用于锁容器其中一部分数据,当多线程访问容器里不同数据段的数据时,线程间就不会存在锁竞争,从而可以有效的提高并发访问效率。它的原理是将数据分成一段一段的存储,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据时,其他段的数据也能被其他线程访问。有些方法需要跨段,如size()和containsValue(),他们可能需要锁定整个表而不仅是某个段,这需要按顺序锁定所有段,操作完毕后又按顺序释放所有段的锁。
BlockingQueue
阻塞队列是生产者-消费者的一个实现,当队列满了时,再次调用put函数添加元素,那么调用线程将会阻塞,直到队列不再是填满状态。避免了手动判断以及同步操作。
函数名 | 作用 |
---|---|
add(e) | 把元素e添加到BlockingQueue里,如果BlockingQueue可以容纳,则返回true,否则抛异常 |
offer(e) | 把元素e添加到BlockingQueue里,如果BlockingQueue可以容纳,则返回true,否则返回false |
offer(e,time,unit) | 把元素e添加到BlockingQueue里,如果BlockingQueue可以容纳,则返回true,否则在等待指定的时间之后继续尝试添加,如果失败则返回false |
put(e) | 把元素e添加到BlockingQueue里,如果BlockingQueue不能容纳,则调用此方法的线程被阻塞直到BlockingQueue里面有空间再继续添加 |
take() | 取走BlockingQueue里排在队首的对象,若BlockingQueue为空,则进入等待状态直到BlockingQueue有新的对象被加入为止 |
poll(time,unit) | 取出并移除队列中的队首元素,如果设定的阻塞时间内还没有获得数据,那么返回null |
element() | 获取队首元素,如果队列为空,那么抛出NoSuchElementException异常 |
peek() | 获取队首元素,如果队列为空,那么返回null |
remove() | 获取并移除队首元素,如果队列为空,那么抛出NoSuchElementException异常 |
BlockingQueue多种常用实现:
- ArrayBlockingQueue 数组实现的、线程安全的、有界的阻塞队列
按FIFO(先进先出)原则对元素进行排序,元素从尾部插入到队列,从头部开始返回。 - LinkedBlockingQueue 单向链表实现的队列
按FIFO(先进先出)原则对元素进行排序,元素从尾部插入到队列,从头部开始返回。吞吐量高于ArrayBlockingQueue,但是在大多数并发应用程序中其可预知的性能要低,功能类似的有ConcurrentLinkedQueue - LinkedBlockingDeque 双向链表实现的双向并发阻塞队列
同时支持FIFO和FILO,即可以从队列的头和尾同时操作(插入/删除),支持线程安全。可以指定队列容量(默认容量大小等于Integer.MAX_VALUE)
内容总结
以上是互联网集市为您收集整理的Java多线程:同步集合与同步锁全部内容,希望文章能够帮你解决Java多线程:同步集合与同步锁所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。