Java并发编程的艺术笔记(六)

6. Java并发容器和框架

1. ConcurrentHashMap

高效的线程安全的hash表,HashTable实现的是表锁,而concurrentHashMap实现的是分段所,意味着同一时间可以有多个线程操作Hash表,提高了效率。

Segment继承了Lock,是分段式锁,一个Segment包含一个HashEntry数组,每个HashEntry是一个链表结构的元素,一个Segment守护着一个HashEntry数组中的元素,对HashEntry数组中元素修改时,必须先获得与他对应的Segment锁。

segments默认长度16,每个segment负载因子0.75,cap(每个segment长度)为1。

  • get():不会对段加锁,因为value是用volatile来修饰的,保证了可见性,提高了效率。只有写才会进行加锁操作。

  • put():插入操作必须加锁,然后第一步是判断是否对HashEntry进行扩容,第二步是添加元素。

    ConcurrentHashMap扩容机制:

    插入元素前判断是否需要扩容,而HashMap是插入元素后再判断是否扩容。ConcurrentHashMap具有的优势就是避免了最后添加元素时进行了无用的扩容。

    ConcurrentHashMap不会对整个容器进行扩容,而只是对某个segment进行扩容。

  • size:ConcurrentHashMap判断size时,会统计Segment的count(volatile变量)。但是如果累加后的segment大小发生变化怎么办?为了安全,ConcurrentHashMap会先尝试两次不加锁的统计count,如果发生了变化,则加锁统计。(判断发生变化的方法就是modCount——修改次数是否发生变化)。

  • 如果想要获得size,建议使用mappingCount方法,返回long类型。

2. ConcurrentLinkedQueue

基于链接节点的无界线程安全队列,FIFO规则,采用CAS算法,所以是非阻塞队列。

减少更新尾节点的次数,可以减少CAS开销。

入队方法永远返回true,所以不能通过返回值判断元素是否入队成功。

3. 阻塞队列

阻塞队列就是支持两个附加操作的队列,指的是支持阻塞的插入和移除方法。

  • 当队列满时,队列会在阻塞插入元素的线程,直到队列不满
  • 队列为空时,获取元素的线程会等待队列变为非空

阻塞队列常用于生产者消费者模式

JDK提供了7种阻塞队列

  • ArrayBlockingQueue:数组结构组成的有界阻塞队列,默认情况下不保证公平性(阻塞的线程都可以争夺锁——为了提高吞吐量)。
  • LinkedBlockingQueue:链表结构组成的有界阻塞队列,默认长度和最大长度均为Integer.MAX_VALUE。
  • PriorityBolockingQueue:支持优先级排序的无界阻塞队列,默认情况下采用升序排列,也可以实现compareTo方法来指定顺序。但是不能保证同优先级的顺序。
  • DelayQueue:使用优先级队列实现的无界阻塞队列,支持延时获取队列元素,可以指定多久可以获取当前元素。可以作为缓存系统的设计、定时任务调度,实现类TimerQueue,ScheduledThreadPoolExecutor中ScheduledFutureTask。
  • SynchronousQueue:不存储元素的阻塞队列,每一个put操作都要等待一个take操作,否则不能继续添加元素。可以创造公平性访问。负责把生产者线程处理的数据直接传给消费者线程,非常适合传递场景。吞吐量也高于ArrayBlockingQueue和LinkedBlockingQueue。
  • LinkedTransferQueue:链表结构组成的TransferQueue队列,相比较其他的,LinkedTransferQueue多了tryTransfer和transfer方法。
  • LinkedBlockingDeque:由链表组成的双向阻塞队列,多线程同时入队列时,减少了一半的竞争。

4. Fork/Join

Fork/Join是任务分割和任务合并,相当于递归对线程。继承RecursiveTask的任务,交给ForkJoinPool处理。

RecursiveTask抽象类->ForkJoinTask抽象类->Future接口


import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

//递归任务
public class CountTask extends RecursiveTask<Integer> {

    private static final int THRESHOLD = 2 ;

    private int start ;

    private int end ;

    public CountTask(int start, int end) {
        this.start = start;
        this.end = end;
    }


    @Override
    protected Integer compute() {

        System.out.println(start + "  " + end);
        int sum = 0 ;

        boolean canCompute = (end - start) <= THRESHOLD ;

        if (canCompute){

            for (int i=start ; i<=end ; i++){
                sum += i ;
            }

        }else{

            int middle = (start + end) / 2 ;
            
            CountTask leftTask = new CountTask(start , middle) ;
            CountTask rightTask = new CountTask(middle+1 , end) ;

            leftTask.fork() ;
            rightTask.fork() ;

            int leftResult = leftTask.join() ;
            int rightResult = rightTask.join() ;

            sum = leftResult + rightResult ;
        }

        return sum ;
    }


    public static void main(String[] args) {
        ForkJoinPool forkJoinPool = new ForkJoinPool() ;

        CountTask countTask = new CountTask(1 , 10) ;

        Future<Integer> result = forkJoinPool.submit(countTask) ;

        try {
            int x = result.get() ;
            System.out.println(x);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (ExecutionException e) {
            e.printStackTrace();
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值