版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wd2014610/article/details/85308038
在Java中绝大部分的集合像什么ArrayList、HashMap等绝大部分不是线程安全的。仅有的线程安全的实现,像HashTable、Vector等在性能上又不好。但是不要怕啊。我们大Java还有并发包(Java.util.concurrent)啊,为高度并发需求提供了全面安全的支持。
一、在传统的集合框架中,如何解决线程安全问题。
当然,除了Hashtable等同步容器,我们可以使用同步包装器创建一个线程安全的容器。但是这种方式用的是非常粗的同步方式,在高并发情况下,性能比较低下。
具体的位置如下
下面楼主写了有一些实践的代码:
package com.newframe.controllers.api;
import java.util.*;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.SynchronousQueue;
/**
* 测试传统线程安全的集合类
*/
public class TestTraditionSyn {
public static void main(String[] args) {
/**
* 1。在传统的集合框架中。
* 除了Hashtable这个是线程安全的同步容器。
* 他的实现基本上就是将Put、get、size等各种
* 方法的操作加上"synchronized"。这就导致了所有的并发操作都在竞争同一把锁
* 一个线程在进行同步操作时,其他线程只能等待,大大降低了并发执行的效率
* (当然这个因为同步的线程开销较大,不推荐使用)
* 还可以通过调用Collections工具类提供的包装类。来构造线程安全的同步包装容器,如下所示
*/
//构造一个线程安全的List
List<String> list = Collections.synchronizedList(new ArrayList<>());
list.add("hello");
list.add("world");
Iterator iterator1 = list.iterator();
while (iterator1.hasNext()){
System.out.println(iterator1.next());
}
//构造一个线程安全的Map
Map<String,Object> map1 = Collections.synchronizedMap(new HashMap<>());
map1.put("1","wang");
map1.put("2","dong");
map1.forEach((key,value) ->{
System.out.println("map1:" + key + "," + value);
});
}
}
二、重头戏首选的肯定还是我们的Java并发包啊
具体位置如下:
下面楼主也写了一些示范如何使用的简单代码:
package com.newframe.controllers.api;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.SynchronousQueue;
/**
* 测试并发包中的集合类
*/
public class TestConcurrentSyn {
public static void main(String[] args) {
/**
* 2。并发包。在工作中,我们更加普遍的是选择利用并发包提供
* 适合在高度并发的环境下使用
* 线程安全容器类
* 这个只要你是按照并发包的标准创建的集合,都是线程安全的。
*/
//关于map的ConcurrentHashMap
ConcurrentHashMap<String,Object> map2 = new ConcurrentHashMap<>();
map2.put("1","我是并发包直接构建的");
map2.put("2","我是线程安全的Map容器,ConcurrentHashMap");
map2.forEach((key,value) ->{
System.out.println("map2:" + key + "," + value);
});
//关于list的CopyOnWriteArrayList
CopyOnWriteArrayList<Integer> list2 = new CopyOnWriteArrayList<>();
list2.add(67612);
list2.add(67362);
list2.forEach(list ->{
System.out.println(list);
});
/**
* 并发包中的线程安全队列
*/
//ArrayBlockingQueue
ArrayBlockingQueue<String> arrayBlockingQueue = new ArrayBlockingQueue(10);
arrayBlockingQueue.add("1");
arrayBlockingQueue.add("3");
arrayBlockingQueue.forEach(queue->{
System.out.println(queue);
});
}
}
三、关于Java8以后的ConcurrentHashMap的一点思考。
ConcurrentHashMap的设计实现是一直都在不断的演化,性能也是在不断的提高。
早期的ConcurrentHashMap,其实现主要是基于:
- 分离锁。在内部进行分段(Segment),里面则是HashEntry的数组,和HashMap类似,哈希相同的条目也是以链表的形式存放。
- HashEntry内部使用volatile的value字段来保证可见性。
那么在Java8中,这个有什么变化呢?
- 在结构上,虽然仍然有Segment定义,但是仅仅是为了给旧版本兼容。初始化已经改成了Lazy-load的形式了,有效避免了初始化开销。
- 数据存储利用的是Volatile来保证可见性。如下图:
好啦,关于这个里面的东西太多了,还需要深入研究,当然,在工作中如果能明确应用场景,做出正确的选择才是关键。