并发6-集合

非阻塞式集合(Nom-Blocking Collection) ConcurrentLikedDeque

  这类集合也包括添加和溢出数据的方法。如果方法不能立即被执行,则返回null或者抛出异常,但是调用这个方法的线程不会阻塞

阻塞式集合(Blocking Collection) LikeBlockingDeque

  当即和已满或者为空时,被调用的添加或者移除方法就不能立即执行,那么调用的这个方法的线程将被阻塞,知道该方法可以被执行

ConcurrentHashMap前需要了解HashMap的数据结构

HashMap=数组+链表:HashMap里面就是一个类似数据的小桶(长度为3的数组,[0]:key  [1]:value  [2]:指向下一个,如果是最后一个则[3]:null)

JDK1.8之前如果HashMap里面的数量是100万,那么如果要拿到其中一个,就很耗费性能,但是在JDK1.8(包含)之后有了红黑树的感念,当HashMap里面的数据达到分配HashMap内存的75%后会以3倍的容量进行扩展。

private static final int MAXIMUM_CAPACITY = 1 << 30; 最大容量2的30次幂
private static final int DEFAULT_CAPACITY = 16; 默认容量16
static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; 最大数组数量0x7fffffff(16进制)==2147483647(十进制)-8
private static final int DEFAULT_CONCURRENCY_LEVEL = 16;默认并发级别
private static final float LOAD_FACTOR = 0.75f;扩展因子
static final int TREEIFY_THRESHOLD = 8;阈值,当小8时为链表,当大于等于8时为红黑树
static final int MIN_TREEIFY_CAPACITY = 64;红黑树最小扩展
我们可以一下put方法中的putval会发现出现了一个Synchronized关键字。其中里面还有一个静态segment内部类,它的意思就是分割的意思,默认HashMap里面的每一个元素都是进行分割。

可以想一下,为什么使用Synchronized锁而不在使用segment进行加锁了呢?经过百度查看大佬们的博客后总结:JDK1.8(不包含)之前,用的是segment方式继承ReentrantLock,实现的原理是将集合进行分割开
来再进行加锁,粒度比较大。而1.8之后使用Synchronized所得是链表中node(指的是一个元素对象)所以锁的更细了,粒度变小了。那么出现并发争抢的可能性还高吗?(当然会出现但是几率十分小)当多个线程争
抢一个node对象的时候,其中一个线程再使用node对象,其他线程只是在进行自旋,如果自旋数量达到阈值会形成线程阻塞(最糟糕的状态)。但是ReentrantLock就不一样它是先getState(CAS)然后进行线程占有,
如果没有占有到线程,那么他会直接挂起等待下次再获取。当然没获取到的时候可以进行TryLock,但是如果尝试获取锁超时或者当前线程直接中断了怎么办?难道在put的时候直接抛出InterruptedException异常?
这样就尴尬了。说白了就是1.8的升级更合理,更优化。从原来的1/16线程使用segment对象变成了1/N线程使用当前node节点对象。当N<16的时候锁的粒度上远远小于segment,阻塞的情况要小很多很多。

ConcurrentLinkedDeque

public class ConllectionDemo1 {
    private static ConcurrentLinkedDeque<String> cld = new ConcurrentLinkedDeque<String>();

    public static void main(String args[]) throws InterruptedException {
        Thread[] add = new Thread[100];
        for (int i = 0; i < 100; i++) {
            add[i] = new Thread(()->{
                for (int j = 0; j < 10000; j++) {
                    cld.add("element"+j);
                }
            });
            add[i].start();
            add[i].join();
        }
        System.out.println("ADDcld:"+cld.size());


        for (int i = 0; i <100 ; i++) {
            Thread[] poll = new Thread[100];
            poll[i] = new Thread(()->{
                for (int j = 0; j <5000 ; j++) {
                    cld.pollLast();
                    cld.pollFirst();
                }
            });

            poll[i].start();
            poll[i].join();

        }
        System.out.println("POllcld:"+cld.size());

    }
}

  

LinkedBlockingDeque

public class LinkBlockingQueueDome {
    private static LinkedBlockingDeque<String> list = new LinkedBlockingDeque<String>(3);

    public static void main(String args[]){
        Thread[] threads = new Thread[3];

        //开始往里面加
        for (int i =0; i <3 ; i++) {
            threads[i] = new Thread(()->{
                for (int j = 0; j <5 ; j++) {
                    try {
                        list.put(j+"");
                        System.out.println(j+"里面的数量是::"+list.size()+"-----");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            });
            threads[i].start();
        }


        //开始从里面去
        for (int i = 0; i <5 ; i++) {
            for (int j = 0; j <3 ; j++) {
                try {
                    TimeUnit.SECONDS.sleep(1);
                    String str = list.take();
                    System.out.println("取出来的list:"+(str)+"里面的数量是:"+list.size());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("end:"+list.size());

    }
}

  

LinkedTransferQueue  生产-消费

PriorityBlockingQueue  优先级

原子操作熟悉CAS,但是需要避免ABA问题,所以我们使用AtomicStampedReference具有状态位的原子性类进行处理

原文地址:https://www.cnblogs.com/gnwzj/p/10611508.html