JAVA高并发集合详解
- Queue(队列)
主要是为了高并发准备的容器
Deque:双端队列,可以反方向装或者取 - 最开始jdk1.0只有Vector和hashtable 默认所有方法都实现了synchronized锁,线程安全但性能比较差,因此后续SUN意识到这个问题之后加了完全没加锁的hashmap,但是由于Hashmap完全没锁,SUN又想到能不能让Hashmap在有锁的时候用呢,此时添加了Collection,里面有一个Collection.synchronizedMap(new HashMap()),将Hashmap变成了加锁的版本,里面锁的粒度变小了,能部分提高性能,在JUC之后,新出来了ConcurrentHashtable由于Hashtable和Vectoy是古老的版本带过来的,所以现在基本不用,知道就行。
1、HashTable性能测试
- public class Constants {
- public static final int COUNT = 1000000;
- public static final int THREAD_COUNT = 100;
- }
- public class T01_TestHashtable {
- static Hashtable<UUID, UUID> m = new Hashtable<>();
- static int count = Constants.COUNT;
- static UUID[] keys = new UUID[count];
- static UUID[] values = new UUID[count];
- static final int THREAD_COUNT = Constants.THREAD_COUNT;
- static {
- for (int i = 0; i < count; i++) {
- keys[i] = UUID.randomUUID();
- values[i] = UUID.randomUUID();
- }
- }
- static class MyThread extends Thread {
- int start;
- int gap = count/THREAD_COUNT;
- public MyThread(int start) {
- this.start = start;
- }
- @Override
- public void run() {
- for(int i=start; i<start+gap; i++) {
- m.put(keys[i], values[i]);
- }
- }
- }
- public static void main(String[] args) {
- long start = System.currentTimeMillis();
- Thread[] threads = new Thread[THREAD_COUNT];
- for(int i=0; i<threads.length; i++) {
- threads[i] =
- new MyThread(i * (count/THREAD_COUNT));
- }
- for(Thread t : threads) {
- t.start();
- }
- for(Thread t : threads) {
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- long end = System.currentTimeMillis();
- System.out.println(end - start);
- System.out.println(m.size());
- //-----------------------------------
- start = System.currentTimeMillis();
- for (int i = 0; i < threads.length; i++) {
- threads[i] = new Thread(()->{
- for (int j = 0; j < 10000000; j++) {
- m.get(keys[10]);
- }
- });
- }
- for(Thread t : threads) {
- t.start();
- }
- for(Thread t : threads) {
- try {
- t.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- end = System.currentTimeMillis();
- System.out.println(end - start);
- }
- }
相对来说,插入的时候ConcurrentHashMap相对于HashMap效率没有明显提高,但是读取的时候效率高很多。
- public class Constants {
- ConcurrentSkipListMap 跳表结构,由于CAS在Tree这种结构上操作太复杂,然后冒出了跳表这种结构,底层时链表,为了查找效率,提取关键元素,制作新的链表,找的时候先找11->78,发现元素比78小,则与11比较,如果大,则在11-78中找,明显效率提高了。
跳表: - 多线程尽量使用Queue,少用List、Set
- CopyOnWrite,写时复制,读的时候不加锁,写的时候加锁,原理是:会在原来基础上Copy一个出来数组,在新的数组上写,写完后将引用指向新数组,过程中读的时候读的是原来数组,当写的过程结束之后读得是新数组,写时复制
- CopyOnWriteArrayList,写(添加)的时候是加了锁的,里面先获取原数组的长度,创建新数组长度为原数组+1,读(get)的时候是加了锁的
- 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();
- }
- }
- public E get(int index) {
- return get(getArray(), index);
- }
- public boolean add(E e) {
- BlockingQueue 为线程池做准备,重点在blocking上,阻塞队列,有很多对多线程友好的方法。
1、常用的一共三种,分别是无界的LinkedBlockingQueue(表示用Linked实现是,无界,除非内存溢出,添加方法put的区别是,put方法非要添加,如果满了就阻塞住,取方法 take(),非要取,如果没有了就阻塞,阻塞的原理就是实现了park(),线程阻塞住,进入wait状态)、
2、有界的ArrayBlockingQueue(表示用Array实现是,有界,除非内存溢出,满了之后程序会阻塞住,等消费者)、
3、DelayQueue(实现时间上的排序)、
4、synchronusQueue(实现线程中间传输内容、任务)、
5、TransferQueue(是前面几种的组合,也可以传输任务,并且不是传递一个,可以传递多个) - ConcurrentLinkedQueue
里面一些友好的方法添加 offer()(返回值是Boolean类型,true表示成功,)、peak()(取) - Queue 与 List的区别,主要在于Queue添加了很多对线程友好的API,比如offer peek poll
这其中一个子类BlockingQueue在上面(offer peek poll)的基础上添加了put take ->主要是实现了阻塞,可以自然而然的实现任务队列,也就是生产者、消费者模型,这是多线程中最重要的模型,是MQ的基础(必问内容) - DelayQueue,可以实现等待时间,类实现Delayed接口,同时设置运行时间runningtime,时间等待越短的优先运行,实现Compare接口,重写方法来确定任务执行队列
按时间进行任务调度。本质是priorityQueue。
- static BlockingQueue<MyTask> tasks = new DelayQueue<>();
- static Random r = new Random();
- static class MyTask implements Delayed {
- String name;
- long runningTime;
- MyTask(String name, long rt) {
- this.name = name;
- this.runningTime = rt;
- }
- @Override
- public int compareTo(Delayed o) {
- if(this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS))
- return -1;
- else if(this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS))
- return 1;
- else
- return 0;
- }
- @Override
- public long getDelay(TimeUnit unit) {
- return unit.convert(runningTime - System.currentTimeMillis(), TimeUnit.MILLISECONDS);
- }
- @Override
- public String toString() {
- return name + " " + runningTime;
- }
- }
main方法:
- public static void main(String[] args) throws InterruptedException {
- long now = System.currentTimeMillis();
- MyTask t1 = new MyTask("t1", now + 1000);
- MyTask t2 = new MyTask("t2", now + 2000);
- MyTask t3 = new MyTask("t3", now + 1500);
- MyTask t4 = new MyTask("t4", now + 2500);
- MyTask t5 = new MyTask("t5", now + 500);
- tasks.put(t1);
- tasks.put(t2);
- tasks.put(t3);
- tasks.put(t4);
- tasks.put(t5);
- System.out.println(tasks);
- for(int i=0; i<5; i++) {
- System.out.println(tasks.take());
- }
- }
结果:
- t5 1587472415617
- t1 1587472416117
- t3 1587472416617
- t2 1587472417117
- t4 1587472417617
- PriorityQueue,会默认对任务排序,最小最优先,里面是小顶堆
Demo:
- public class T07_01_PriorityQueque {
- public static void main(String[] args) {
- PriorityQueue<String> q = new PriorityQueue<>();
- q.add("c");
- q.add("e");
- q.add("a");
- q.add("d");
- q.add("z");
- for (int i = 0; i < 5; i++) {
- System.out.println(q.poll());
- }
- }
- }
- //打印结果:a、c、d、e、z
- public class T07_01_PriorityQueque {
- synchronusQueue,容量为0,不是用来装东西,主要是用来一个线程给另一个线程下达任务,与之前的exchanger容器类似,不可以用add方法,里面容器为0,不可以装东西。应用场景是:做某件事需要等待结果完成才继续执行接下来的任务。
JAVA高并发集合详解的更多相关文章
- 《JAVA高并发编程详解》-七种单例模式
- 《JAVA高并发编程详解》-volatile和synchronized
- 《JAVA高并发编程详解》-并发编程有三个至关重要的特性:原子性,有序性,可见性
- 《JAVA高并发编程详解》-类的加载过程简介
- 《JAVA高并发编程详解》-wait和sleep
- 《JAVA高并发编程详解》-程序可能出现死锁的场景
- 《JAVA高并发编程详解》-Thread start方法的源码
Thread start方法的源码:
- 《JAVA高并发编程详解》-Thread对象的启动
当我们用关键字new创建一个Thread对象时,此时它并不处于执行状态,因为没有调用start方法启动该线程,那么线程的状态为NEW状态,NEW状态通过start方法进入RUNNABLE状态. 线程一 ...
- Java中的集合详解及代码测试
1:对象数组 (1)数组既可以存储基本数据类型,也可以存储引用类型.它存储引用类型的时候的数组就叫对象数组. 2:集合(Collection) (1)集合的由来 我们学习的是Java -- 面向对象 ...
随机推荐
- 5分钟教你在Linux下安装VMware
如果我们只有一台笔记本,又想要搭建一个小集群,怎么办?虚拟机帮你实现梦想,市面上较为常用的虚拟机软件有VMware.VirtualBox.Xen.KVM.hyper-v等,本文主要介绍如何在Linux ...
- @Autowired 和@Resource区别
二者都可以用在bean的注入时,但是@Autowired是spring提供的注解,而@Resource是javax包中的. 默认情况下,@Autowired注入规则是byType,通过类型注入:当然, ...
- 01 . Go之从零实现Web框架(类似Gin)
设计一个框架 大部分时候,我们需要实现一个 Web 应用,第一反应是应该使用哪个框架.不同的框架设计理念和提供的功能有很大的差别.比如 Python 语言的 django和flask,前者大而全,后者 ...
- web攻防环境--一句话木马
任务一.基于centos7搭建dvwa web服务靶机 1.在centos7安装LAMP并启动,访问phpinfo页面 也即安装httpd.php.mysql服务. 直接进行yum安装即可,完成后检查 ...
- 聊一聊这个总下载量36039K的XSS-NPM库,是如何工作的?
上篇文章这一次,彻底理解XSS攻击讲解了XSS攻击的类型和预防方式,本篇文章我们来看这个36039K的XSS-NPM库(你没有看错就是3603W次, 36039K次,36,039,651次,数据来自h ...
- java面向对象的一些知识
(1)this和super关键字的用法 this表示调用本类实例方法和成员变量,this引用就是对一个对象的引用,如this.name 表示本类成员变量name,静态方法中不能使用this关键字. ...
- Miller Rabin素数检测与Pollard Rho算法
一些前置知识可以看一下我的联赛前数学知识 如何判断一个数是否为质数 方法一:试除法 扫描\(2\sim \sqrt{n}\)之间的所有整数,依次检查它们能否整除\(n\),若都不能整除,则\(n\)是 ...
- 对接口报错404 发现url多了一些不可描述的代码%E2%80%8B
接口url出现了空格,复制的url可能出现空格肉眼看不出来,手动输入一遍URL
- 使用Pr压缩视频,增大音量
视屏压缩(亲测有效教程):https://www.zhihu.com/question/67762625 音量增大(亲测有效教程):https://jingyan.baidu.com/article/ ...
- 死磕以太坊源码分析之state
死磕以太坊源码分析之state 配合以下代码进行阅读:https://github.com/blockchainGuide/ 希望读者在阅读过程中发现问题可以及时评论哦,大家一起进步. 源码目录 |- ...