多线程与高并发(四) - 容器

24 篇文章 0 订阅
12 篇文章 0 订阅

目录

day6

一,java 容器

二,MAP

1,linkedHashMap

2,TreeMap

3,ConcurrentSkipListMap

三,Queue 

1,从Vector 到 Queue

2,CopyOnWriteArrayList

3,ConcurrentLinkedQueue

         4,BlockingQueue

4.1 LinkedBlockingQueue

4.2,ArrayBlockingQueue

5,DelayQueue

6,SynchronousQueue

7,TransferQueue

day6

一,java 容器

物理的存储只有数组和链表

二,MAP

Vector,HashTbale jkd1.0 设计理念就是同步线程安全的,所以每个方法都是加了锁的synchronized。但多数的时候都是在单线程工作,这种情况下是不需要线程安全的。

HashMap 完全没有加锁,新的Map容器比HashTbale好用,但是它又没有加锁,怎么让它既支持锁的环境又支持非锁的环境呢?又添加了一个Collections的工具类,其中有一个synchronizedMap会把HashMap变成有锁的版本。

由于设计上的缺陷Vector,HashTbale 基本不用。

HashMap 与HashTbale 区别在于,HashMap 内部声明了一个类用于加锁,虽然也是用的synchronized,但是锁的粒度上更细,效率上略高于HashTbale 。

后期又添加了一个效率更高的Map:ConcurrentHashMap。

性能测试

java Map 读写性能测试_解决问题no解决代码问题的博客-CSDN博客

1,linkedHashMap

线程安全,无序的

是在HashMap的基础上添加了链表,加快了访问便利的效率;

2,TreeMap

线程不安全,无序的

红黑树,本身是排序的,查找的时候效率比较高

是为了实现拍好顺序之后查询快速,插入的效率也不是特别低

3,ConcurrentSkipListMap

线程安全,有序,跳表

由于在红黑树上实现用C+操作比较复杂,用跳表代替了

底层是链表,找到关键元素形成一个链表,减少的是检索的次数,同时CAS的实现又比红黑树结构的容易很多。

三,Queue 


阻塞队列,为了高并发做准备
一端进数据,一端取数据,取的顺序不一定是进数据的顺序,需要看具体实现
PriorityQueue : 排序的队列
DelayQueue   : 按任务到期的顺序,优先快过期的任务
Deque :双端队列

1,从Vector 到 Queue

多线程下,如果有重复的数据,多考虑Queue而不是List。

双方的区别就是Queue添加了很多对线程友好的API  offer prrk poll

BlockingQueue put take 阻塞

2,CopyOnWriteArrayList

场景:写的少,读的多

写时复制

当需要往容器种添加元素的时候,把底层的array复制出来长度+1并把要添加的元素放在末尾,最后把读的引用指向新的array。(需要避免每次使用size)

只有add需要加锁

3,ConcurrentLinkedQueue

全是线程安全的操作

offer (add) 返回一个boolean,add加不进去会返回异常

peek 取  并不会removes

poll    取 会removes

4,BlockingQueue

4.1 LinkedBlockingQueue

阻塞队列,让线程实现自动的阻塞

无界(无界是相对的,size方法返回int类型,最大长度对应也就是int的最大值):链表实现的,没有长度限制,能添加到内存溢出

put  :添加数据,一定要添加进去,如果容器满了就阻塞

take :获取数据,一定要获取数据,如果容器是null的情况就阻塞

天生就是消费者生产者的模型

阻塞的底层是用的LockSupport.park(this);

 

4.2,ArrayBlockingQueue

有界:可以指定容器size

5,DelayQueue

实现在时间上的排序

场景:按时间进行任务调度

package com.example.demo.thread.container;

import java.util.concurrent.DelayQueue;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;

/**
 * DelayQueue是用的PriorityQueue实现的
 * 加了阻塞相关的API
 */
public class T4_DelayQueue {

    static DelayQueue<MyTask> queue = new DelayQueue();

    static class MyTask implements Delayed{
        String name;
        long runningTime;

        MyTask(String name, long runningTime){
            this.name = name;
            this.runningTime = runningTime;
        }

       @Override
       public long getDelay(TimeUnit unit) {
           return unit.convert(runningTime - System.currentTimeMillis(),TimeUnit.MILLISECONDS);
       }

       @Override
       public int compareTo(Delayed o) {
            //毫秒,这个让我联想到了BigDecimal
           if (this.getDelay(TimeUnit.MILLISECONDS) < o.getDelay(TimeUnit.MILLISECONDS)) {
                return -1;
           }else if (this.getDelay(TimeUnit.MILLISECONDS) > o.getDelay(TimeUnit.MILLISECONDS)) {
                return 1;
           }
           return 0;
       }

        @Override
        public String toString() {
            return name+"    "+runningTime;
        }
    }


    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+1500);
        MyTask t3 = new MyTask("t3",now+500);
        MyTask t4 = new MyTask("t4",now+1500);

        queue.put(t1);
        queue.put(t2);
        queue.put(t3);
        queue.put(t4);

        System.out.println("queue = " + queue);

        for (int i = 0; i < 4; i++) {
            System.out.println(queue.take());
        }

    }
}

Console:

queue = [t3    1648998055494, t2    1648998056494, t1    1648998055994, t4    1648998056494]
t3    1648998055494
t1    1648998055994
t4    1648998056494
t2    1648998056494

6,SynchronousQueue

容量为0,在两个线程之间实现传递数据

没人在前面等着,就无法往queue里面添加

阻塞等待消费者消费

package com.example.demo.thread.container;

import java.util.concurrent.SynchronousQueue;

public class T6_SynchronousQueue {
    public static void main(String[] args) throws InterruptedException {
        SynchronousQueue<String> queue = new SynchronousQueue<>();
        //没人在前面等着,就无法往queue里面添加
//        queue.put("jjj");
//        System.out.println(queue.size());
        new Thread(()->{
            try {
                System.out.println("t1  "+queue.take());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }).start();;

        queue.put("jjj");//阻塞等待消费者消费
//        queue.add("aaa");//java.lang.IllegalStateException: Queue full
        System.out.println(queue.size());
    }
}

7,TransferQueue

给线程传递数据,不同与SynchronousQueue只能传递一个的是,可也传递好多个

如果用put跟其他就没什么不同了

transfer :  装完 阻塞等着别人取走(等结果) 在执行,必须先启动消费者线程

场景:等结果,付账有结果之后,才走下一步返结果。手机银行转账的场景,有读秒,然后返回是否成功。

 交替打印T3

package com.example.demo.thread.t5mst;

import java.util.concurrent.LinkedTransferQueue;
import java.util.concurrent.TransferQueue;
import java.util.concurrent.locks.LockSupport;

public class T3TransferQueue {

    static TransferQueue<String> queue = new LinkedTransferQueue<>();
    
    public static void main(String[] args) {
        String[] strs = new String[]{"A","B","C","D","E"};

        new Thread(()->{
            for (;;)
                try {
                    System.err.println(Thread.currentThread().getName() +  "  " + queue.take());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

        },"t1").start();

        new Thread(()->{
            for (int i = 0; i < strs.length ; i++) {
                System.err.println(Thread.currentThread().getName() + "  " + strs[i]);
                try {
                    queue.transfer(i+"");
                    Thread.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        },"t2").start();

    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值