HashMap
HashMap类实现了Map接口,所以实现了Map常用的一些方法,Map通常在java开发中被称为字典,是一种保存数据的容器;HashMap是基于哈希表实现的,每一个元素是一个key-value对。HashMap在JDK1.8之前的实现方式 数组+链表,但是在JDK1.8后对HashMap进行了底层优化,改为了由 数组+链表+红黑树实现,主要的目的是提高查找效率。
HashTable
Hashtable 是一个散列表,它存储的内容是键值对(key-value)映射;继承于Dictionary,实现了Map、Cloneable、java.io.Serializable接口,它的函数都是同步的,这意味着它是线程安全的。它的key、value都不可以为null。
HashMap VS HashTable
HashMap | HashTable | |
---|---|---|
线程安全 | 否 | 是 |
键值可为空 | 可 | 否 |
效率 | 高 | 低 |
HashTable为什么线程安全?
HashTable线程安全是因为它所有的函数都是线程同步的,即同时只允许一个线程在操作HashTable
,这样保证了他的线程安全性
,由于对整个HashTable
都上了锁,导致当有线程在操作HashTable
的时候,其他的线程会成阻塞状态,导致效率比较低下。

ConCurrentHashMap
Java 5.0 在java.util.concurrent
包中提供了多种并发容器类来改进同步容器的性能。ConcurrentHashMap 同步容器类是Java 5
增加的一个线程安全的哈希表。对与多线程的操作,介于 HashMap 与 Hashtable 之间。内部采用“锁分段
”机制替代 Hashtable
的独占锁
。进而提高性能。
此包还提供了设计用于多线程上下文中的 Collection 实现:ConcurrentHashMap
、 ConcurrentSkipListMap
、ConcurrentSkipListSet
、CopyOnWriteArrayList
和 CopyOnWriteArraySet
。当期望许多线程访问一个给定 collection 时, ConcurrentHashMap
通常优于同步的HashMap
,ConcurrentSkipListMap
通常优于同步的 TreeMap
。当期望的读数和遍历远远大于列表的更新数时, CopyOnWriteArrayList
优于同步的ArrayList
。

Java 8 中又将
ConCurrentHashMap
的锁分段
机制改用了CAS算法
来实现。因为CAS没有锁,不需要考虑上下文切换的问题,线程也不会阻塞,效率比较高。
并发修改异常
public class CopyOnWriteTest {
public static void main(String[] args) {
CopyOnWriteThread copyOnWriteThread = new CopyOnWriteThread();
for (int i = 0; i < 10; i++) {
new Thread(copyOnWriteThread,"线程"+(i+1)).start();
}
}
}
class CopyOnWriteThread implements Runnable{
public static List<String> list = Collections.synchronizedList(new ArrayList<>());
static {
list.add("AA");
list.add("BB");
list.add("CC");
}
@Override
public void run() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(Thread.currentThread().getName()+":"+iterator.next());
list.add(Thread.currentThread().getName());
}
}
}
会报出
java.util.ConcurrentModificationException
异常
CopyOnWriteArrayList
public class CopyOnWriteTest {
public static void main(String[] args) {
CopyOnWriteThread copyOnWriteThread = new CopyOnWriteThread();
for (int i = 0; i < 10; i++) {
new Thread(copyOnWriteThread,"线程"+(i+1)).start();
}
}
}
class CopyOnWriteThread implements Runnable{
public static CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
static {
list.add("AA");
list.add("BB");
list.add("CC");
}
@Override
public void run() {
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
System.out.println(Thread.currentThread().getName()+":"+iterator.next());
list.add(Thread.currentThread().getName());
}
}
}
CopyOnWriteArrayList/CopyOnWriteArraySet "写入并复制" 每次写入都会复制一个新的列表,因为每次都要复制,所以效率比较低,如果要写入的操作比较多的话就不适用了。并发迭代多时可以适用。
闭锁(CountDownLatch
)
闭锁
:在完成某些运算时,只有其他所有的线程运算全部完成,当前运算才继续执行。
Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能。
CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
闭锁可以延迟线程的进度直到其到达终止状态,闭锁可以用来确保某些活动直到其他活动都完成才继续执行:
- 确保某个计算在其需要的所有资源都被初始化之后才继续执行;
- 确保某个服务在其依赖的所有其他服务都已经启动之后才启动;
- 等待直到某个操作所有参与者都准备就绪再继续执行。
实例:计算多个线程总耗费时间
public class CountDownLatchTest {
public static void main(String[] args) {
final CountDownLatch latch = new CountDownLatch(10);
LatchDemo latchDemo = new LatchDemo(latch);
long start = System.currentTimeMillis();
for (int i = 0; i < 10; i++) {
new Thread(latchDemo).start();
}
/**
* 阻塞线程,等待
*/
try {
latch.await();
}catch (InterruptedException e){
}
long end = System.currentTimeMillis();
System.out.println("耗费时间为:"+(end-start));
}
}
class LatchDemo implements Runnable{
private CountDownLatch countDownLatch;
public LatchDemo(CountDownLatch countDownLatch) {
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
try{
for (int i = 0; i < 50000; i++) {
if (i%10000==0){
System.out.println(Thread.currentThread().getName()+":"+i);
}
}
}finally {
/**
* latch减一
*/
countDownLatch.countDown();
}
}
}
网友评论