8.并发编程-并发容器

ConcurrentHashMap

在这里插入图片描述

1.7的get方法
首先会定位到segment(本身是可重入锁):对key的hashcode进行再散列值的高位取模
然后定位table:key的hashcode进行再次取值取模
依次扫描链表,找到元素返回值,找不到返回null

为什么HashMap在多线程情况下不适用:多线程情况下put方法会引起死循环,hashMap里面的Entry链表会产生环形数据结构,这就导致链表上的下一个节点永远不为空。

位运算

在这里插入图片描述

位运算控制CRUD权限

@Data
public class TestDemo {
    //查询权限
    public static final int ALLOW_SELECT = 1 << 0;//0001
    //插入权限
    public static final int ALLOW_INSERT = 1 << 1;//0010
    //更新权限
    public static final int ALLOW_UPDATE = 1 << 2;//0100
    //删除权限
    public static final int ALLOW_DELETE = 1 << 3;//1000
    //权限状态
    private int flag;

    //设置用户权限
    public void setPer(int per) {
        flag = per;
    }

    //再增加用户权限,1个或者多个
    public void enable(int per) {
        flag = flag | per;
    }

    //删除用户权限
    public void disable(int per) {
        flag = flag & ~per;
    }

    //判断用户权限
    public Boolean isAllow(int per) {
        return ((flag & per) == per);
    }

    //判断用户没有的权限
    public Boolean isNotAllow(int per) {
        return ((flag & per) == 0);
    }

    @Test
    public void testDemo1() {
        //默认全部权限
        int flag = 15;
        TestDemo testDemo = new TestDemo();
        testDemo.setFlag(flag);
        //去除掉删除和更新权限
        testDemo.disable(ALLOW_DELETE | ALLOW_UPDATE);
        System.out.println("ALLOW_SELECT=" + testDemo.isAllow(ALLOW_SELECT));
        System.out.println("ALLOW_DELETE=" + testDemo.isAllow(ALLOW_DELETE));
        System.out.println("ALLOW_UPDATE=" + testDemo.isAllow(ALLOW_UPDATE));
        System.out.println("ALLOW_INSERT=" + testDemo.isAllow(ALLOW_INSERT));
    }
}
ALLOW_SELECT=true
ALLOW_DELETE=false
ALLOW_UPDATE=false
ALLOW_INSERT=true

JDK1.8后的实现

与1.7相比

1.取消了segment数组,直接采用tabe数组,好处在于锁的粒度更小,减少并发冲突的概率。
2.table数组+链表+红黑树的结构。纯链表时间复杂度为O(n),红黑树时间复杂度为O(logn),性能有极大的提示。其次当链表的长度超过8时候,就会将链表转为红黑树

主要数据结构和关键变量

sizeCtl:
1.如果小于0说明进行初始化或者进行扩容。-1表示正在初始化,-n表示有n-1个线程正在进行扩容。
2.等于0表示还没有被初始化
3.大于0的数是表示初始化或者进行下一次扩容阈值
TreeNode,TreeBin
TreeNode用在红黑树下面。TreeBin是实际放在table数组中的,代表这个红黑树的跟。
数据定位
1.7hash算法:高位余上整个hash值,定位到segment,然后用整个hash的key来定位table
1.8hash算法:高位异或hash值,来定位table上的值
红黑树和链表转换
当列表长度大于8个时候,就会转红黑树,当红黑树的长度小于六个时候,会将红黑树转为列表

Put时候做了什么

在这里插入图片描述在这里插入图片描述
在这里插入图片描述
数据的实际初始化
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

Get时候做了什么

在这里插入图片描述

扩容

最终调用transfer进行扩容,而且扩容一定是2n次方的整数(减少移动和重排的次数)

更多并发容器

在这里插入图片描述

ConcurrentSkipListMap, ConcurrentSkipListSet

对应TreeMap和TreeSet有序容器的并发版本。

链表和跳表

链表:普通的链表插入删除效率很快,但是查询缺很慢,如果我要找90所在的位置,那它需要进行20,30,40,50,70,90这几次遍历才能找到90。
跳表(概率数据结构)
1.以空间换取时间类似于数据库的索引。
2.当新增一个元素后,会通过抛色子方式,决定上一层是否为索引,如果为false直至最顶层
3.跳表加快了查询的效率,如果我要查70,它流程是通过顶层的索引往下找。只需要查询三次即可。
在这里插入图片描述

为什么hashMap不适应跳表

跳表操作是采用空间换时间的操作,hashmap的空间利用率很低在30%左右,如果在加上跳表的索引,那么它的空间使用率会急剧下降

ConcurrentLinkedQueue

无界非阻塞队列,底层是链表,遵循先进先出原则。是linkedList的并发版本。
add和offer方法将元素插入到队列的底部,peek拿头部的数据,但不移除,poll拿头部数据并且移除

阻塞队列

在这里插入图片描述
在这里插入图片描述

概念

对队列满的时候,插入的元素的线程将被阻塞,直到队列不满。
当队列为空的时候,获取元素线程被阻塞,直至队列有数据。

生产者和消费者

举个列子:生产者负责生产汽油,消费者负责加油,当汽油的生产量和加油消耗量不成正比时候,这时候会出现两者之间的能力不匹配的问题。所以这时候加一个容器[加油站],每次加油去加油站,而每次生产的汽油放进加油站,解决了加油和生产油两者直接的耦合。

延迟队列实战

使用DelayQueue实现一个订单延超时取消系统

/*
 * 实现一个订单延迟功能,实现Delayed接口。重写compareTo,getDelay方法
 * 这里框架针对于超时取消通用
 * */
public class ItemVo<T> implements Delayed {
    //数据
    private T date;
    //订单的过期时间 ms单位
    private long activeTime;

    public ItemVo(T date, long activeTime) {
        this.date = date;
        //将时间转换为ns,加上当前纳秒数
        this.activeTime = TimeUnit.NANOSECONDS.convert(activeTime, TimeUnit.MILLISECONDS) + System.nanoTime();
    }

    public T getDate() {
        return date;
    }

    public long getActiveTime() {
        return activeTime;
    }
    @Override
    public int compareTo(Delayed o) {
        long d = getDelay(TimeUnit.NANOSECONDS) - o.getDelay(TimeUnit.NANOSECONDS);
        return (d == 0) ? 0 : (d > 0 ? 1 : -1);
    }

    //返回元素剩余的时间
    @Override
    public long getDelay(TimeUnit unit) {
        long d = unit.convert(this.activeTime - System.nanoTime(), TimeUnit.NANOSECONDS);
        return d;
    }
}

/*
* 订单实体类
* */
public class OrderBean {
    //订单号
    private String code;
    //订单金额
    private Integer money;

    public OrderBean(String code, Integer money) {
        this.code = code;
        this.money = money;
    }

    public String getCode() {
        return code;
    }

    public void setCode(String code) {
        this.code = code;
    }

    public Integer getMoney() {
        return money;
    }

    public void setMoney(Integer money) {
        this.money = money;
    }
}

public class TestDemo {
    private DelayQueue<ItemVo<OrderBean>> delayQueue;

    public TestDemo(DelayQueue<ItemVo<OrderBean>> delayQueue) {
        this.delayQueue = delayQueue;
    }

    //插入数据
    public Thread setOrder() {
        return new Thread(() -> {
            //5s后过期
            OrderBean tb = new OrderBean("TB2022040401", 888);
            ItemVo<OrderBean> tbVo = new ItemVo<>(tb, 2000);
            delayQueue.add(tbVo);
            System.out.println("淘宝订单2s后到期:" + tb.getCode());
            //6s后过期
            OrderBean jd = new OrderBean("JD2022040401", 888);
            ItemVo<OrderBean> jdVo = new ItemVo<>(jd, 2000);
            delayQueue.add(jdVo);
            System.out.println("京东订单2s后到期:" + jd.getCode());
        });
    }

    //获取过期的数据
    public Thread getOrder() {
        return new Thread(() -> {
            while (true) {
                try {
                    ItemVo<OrderBean> take = delayQueue.take();
                    OrderBean date = (OrderBean) take.getDate();
                    System.out.println("拿到过期的订单:" + date.getCode());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }

        });
    }

    public static void main(String[] args) {
        DelayQueue<ItemVo<OrderBean>> itemVos = new DelayQueue<>();
        TestDemo testDemo = new TestDemo(itemVos);
        Thread setThread = testDemo.setOrder();
        Thread getThread = testDemo.getOrder();
        setThread.start();
        getThread.start();
    }
}
淘宝订单2s后到期:TB2022040401
京东订单2s后到期:JD2022040401
拿到过期的订单:TB2022040401
拿到过期的订单:JD2022040401

面试常问

在这里插入图片描述

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值