J.U.C包中的工具(ReentrantLock、AQS、CountDownLatch、Semaphore、CyclicBarrier、Condition、ForkJoin)

2.4 J.U.C包中的工具

2.4.1 重入锁ReentrantLock —— 独占锁/互斥锁

互斥性:同一时刻,只允许一个线程进入到加锁的代码中。保证在多线程环境下,线程的顺序访问

重入性:一个持有锁的线程,在释放锁之前,如果再次访问了该同步锁的其他方法,不需要再次争抢锁,只需要记录重入次数。

static Lock lock = new ReentrantLock();
lock.lock();//抢占锁,如果没有抢占到锁,会阻塞
if (lock.tryLock()) { //试图抢占锁,如果没有抢占到锁,不会阻塞
    // 锁抢占成功才会执行这段代码
}
lock.unlock(); //释放锁

如下一段简单演示求和代码,正常情况下结果是1000,可是当多个线程同时执行自增操作,会发现结果可能小于1000。

public class ReentrantLockDemo {
    static Lock lock = new ReentrantLock();
    public static int count = 0;
    public static void incr() {
        try {
            Thread.sleep(1);
            count ++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            new Thread(()->ReentrantLockDemo.incr()).start();
        }
        Thread.sleep(6000);
        System.out.println("result=" + count);
    }
}

针对上面问题,需要加锁,加锁后会发现结果永远都是1000

public class ReentrantLockDemo {
    static Lock lock = new ReentrantLock();
    public static int count = 0;
    public static void incr() {
        lock.lock();//抢占锁
        try {
            Thread.sleep(1);
            count ++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally { //一定要在finally块释放锁,否则万一上面出错,那么就会死锁
            lock.unlock(); //释放锁
        }
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 1000; i++) {
            new Thread(()->ReentrantLockDemo.incr()).start();
        }
        Thread.sleep(6000);
        System.out.println("result=" + count);
    }
}

下面简单代码演示锁的重入特性,两个加锁的方法,一个方法调用另外一个,如果没有重入特性,理论上就会出现死锁,但是使用ReentrantLock,同一个线程再次加锁,只会增加重入次数,不会再次抢占锁,避免死锁的情况出现。

static Lock lock = new ReentrantLock();
public static int count = 0;
public static void incre() {
    lock.lock(); //获得锁
    count ++; //被保护对象
    decr(); //这个时候不需要再次争抢锁
    lock.unlock(); //释放锁
}
public static void decr() {
    lock.lock(); //获得锁
    count --; //被保护对象
    lock.unlock(); //释放锁
}

2.4.2 AQS(AbstractQueuedSynchronizer)

队列同步工具

state被 volatile 关键字修饰,从而保证可见性;

cas方法保证原子性操作;

CAS原子性操作

// Atomically sets synchronization state to the given updated value 
// if the current state value equals the expected value.
protected final boolean compareAndSetState(int expect, int update) {
    return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

public final class Unsafe {
    public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
}

锁的设计思想

  • 一个全局变量来标识锁是否被抢占;
  • 如何抢占锁;
  • 如果抢占到锁:抢占到锁执行业务逻辑;
  • 如果没有抢占到锁:定义一个阻塞队列(双向链表),将抢占锁失败的线程加到队列中排队等候;
  • 如何释放锁:修改全局变量标识,唤醒处于队列中的指定线程;

2.4.3 CountDownLatch —— 共享锁

是一个同步工具类,它允许一个或多个线程一直等待,直到其他线程的操作完成之后再执行,通过倒数计数的方式来判断count=0则释放锁。

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3); //初始化计数count=3
        for (int i = 0; i < 3; i ++) { //那么必须要有3个线程被调用,否则将会一直等待
            new CountDownThread((i+1), countDownLatch).start();
        }
        countDownLatch.await(); //阻塞主线程,直到count计数为0
        System.out.println("主线程继续执行...");
    }
}
public class CountDownThread extends Thread {
    private int num;
    private CountDownLatch countDownLatch;
    public CountDownThread(int num, CountDownLatch countDownLatch) {
        this.num = num;
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        System.out.println("thread" + num + "执行...");
        countDownLatch.countDown(); //倒计时
    }
}

从结果可以看出来,无论三个线程的顺序怎么变,但是主线程总是在三个线程全部执行完毕才会被解锁。 

// 模拟高并发
public class CountDownSimulateConcurrent implements Runnable {
    private static CountDownLatch countDownLatch = new CountDownLatch(1);

    public static void main(String[] args) {
        for (int i = 0; i < 1000; i ++) {
            new Thread(new CountDownSimulateConcurrent()).start();
        }
        countDownLatch.countDown(); //1000个线程全部启动后取消阻塞开始并发运行
    }
    @Override
    public void run() {
        try {
            countDownLatch.await(); //阻塞主线程
            // TODO 要测试高并发的代码
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

CountDown源码分析:CountDown有个内部类Sync继承自AQS

public CountDownLatch(int count) {//将count存入AQS的state
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count); //共享锁
}
Sync(int count) {
    setState(count); //state=count, 如果AQS中state=0则await被唤醒,每次调用countDown,state就会被减一
}

避免死锁 

因为必须保证执行countDown()方法将count变为0才会唤醒阻塞的线程,所以一旦线程数和计数器数量不相等,或者一个线程异常阻塞,没有执行countDown()方法,那么所有线程就会永久阻塞,导致死锁。

为了避免死锁,我们一般会将countDown()方法放在finally块中,保证程序在异常情况下也一定会执行countDown()方法;此外,设置一个带超时时间的await,如果到了超时时间会直接抛异常,从而避免死锁。

public class CountDownLatchDemo {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(3); //初始化计数count=3
        for (int i = 0; i < 3; i ++) { //那么必须要有3个线程被调用,否则将会一直等待
            new CountDownThread((i+1), countDownLatch).start();
        }
        try {
            // 带超时时间,避免长时间阻塞
            countDownLatch.await(20,TimeUnit.SECONDS); //阻塞主线程,直到count计数为0
        } catch (InterruptedException e) {
            e.printStackTrace();
            System.out.println("执行线程异常");
        } finally {
            System.out.println("执行线程关闭");
        }
        System.out.println("主线程继续执行...");
    }
}
public class CountDownThread extends Thread {
    private int num;
    private CountDownLatch countDownLatch;
    public CountDownThread(int num, CountDownLatch countDownLatch) {
        this.num = num;
        this.countDownLatch = countDownLatch;
    }
    @Override
    public void run() {
        try {
            System.out.println("thread" + num + "执行...");
            int a = b / 0;
        } catch(Exception e) {
            e.printStackTrace();
        } finally {
            countDownLatch.countDown(); //倒计时
        }
    }
}

2.4.4 Semaphore —— 共享锁

信号灯,可以控制同时访问的线程的个数,通过acquire获得一个许可,如果没有许可就等待,通过release释放一个许可,类似于限流的作用。

public class SemaphoreDemo {
    public static void main(String[] args) {
        // 限制当前可以获取的最大许可数量是5
        Semaphore semaphore = new Semaphore(5);
        for (int i = 0; i < 10; i ++) {
            new Car((i + 1), semaphore).start();
        }
    }
    static class Car extends Thread {
        private int num;
        private Semaphore semaphore;
        public Car(int num, Semaphore semaphore) {
            this.num = num;
            this.semaphore = semaphore;
        }
        @Override
        public void run() {
            try {
                semaphore.acquire(); //获得许可
                System.out.println("第" + num + "辆车获得一个许可");
                Thread.sleep(2000);
                System.out.println("第" + num + "辆车释放许可");
                semaphore.release(); //释放许可
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

Semaphore源码分析:Semaphore有个内部类Sync继承自AQS

public Semaphore(int permits) { //将许可存入AQS的state
    sync = new Semaphore.NonfairSync(permits);
}
public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1); //获得共享锁
}

2.4.5 CyclicBarrier —— 独占锁

可循用使用屏障,当一组线程到达屏障点时会被阻塞,直到最后一个线程到达屏障点,所有线程的阻塞才会被打开,所有被屏障拦截的线程才会继续工作。

应用场景:当需要所有子任务都完成之后才执行主任务时,这时候可以使用CyclicBarrier。

注意:如果执行的线程数量没有达到定义的parties数量,会导致死锁,所以一般会通过两种方式解决这种问题

a. cyclicBarrier.await(10, TimeUnit.SECONDS);设置过期时间,如果到了过期时间阻塞会被打开。

b. cyclicBarrier.reset();通过reset方法重置计数,会抛出BrokenBarrierException,根据异常进行处理。

public class CyclicBarrierDemo extends Thread {
    @Override
    public void run() {
        System.out.println("主线程开始执行....");
    }
    // 定义了parties=3,那么必须是3个子线程执行完毕也会打开阻塞屏障
    // 如果少于三个子线程,那么会造成死锁
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(3, new CyclicBarrierDemo());
        new CyclicDataImportThread("path1", cyclicBarrier).start();
        new CyclicDataImportThread("path2", cyclicBarrier).start();
        new CyclicDataImportThread("path3", cyclicBarrier).start();
        System.out.println("希望三个子线程全部执行完毕之后再执行主线程后续方法....");
    }
}

public class CyclicDataImportThread extends Thread {
    private String path;
    private CyclicBarrier cyclicBarrier;
    @Override
    public void run() {
        System.out.println(path + "位置数据开始导入...");
        try {
            cyclicBarrier.await(); //阻塞
            System.out.println(path + "数据导入完成...");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } catch (BrokenBarrierException e) {
            e.printStackTrace();
        }
    }
    public CyclicDataImportThread(String path, CyclicBarrier cyclicBarrier) {
        this.path = path;
        this.cyclicBarrier = cyclicBarrier;
    }
}

执行结果: 

实现原理

用到了ReentrantLock去获得锁并且释放锁,通过自循环去判断count的值是否为0,不为0则await,直到所有子线程执行完毕,count为0则breakBarrier,重置count=parties,通过signalAll()唤醒所有被阻塞的线程,并且开启新一轮的generation。

2.4.6 Condition

多线程协调通信的工具类,可以让某些线程一起等待某个条件condition,只有满足条件时,线程才会被唤醒。

condition类似于wait / notify, condition是JUC包中对应Lock的实现,而wait / notify是对应的synchronized。

public class ConditionDemo {
    public static void main(String[] args) {
        Lock lock = new ReentrantLock(); //使用同一个锁
        Condition condition = lock.newCondition(); //使用同一个condition
        ConditionWaitThread conditionWaitThread = new ConditionWaitThread(lock, condition);
        ConditionNotifyThread conditionNotifyThread = new ConditionNotifyThread(lock, condition);
        conditionWaitThread.start();
        conditionNotifyThread.start();
    }
}
public class ConditionWaitThread extends Thread {
    private Lock lock;
    private Condition condition;
    public ConditionWaitThread(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }
    @Override
    public void run() {
        System.out.println("begin - condition wait");
        try {
            lock.lock(); //获得锁
            condition.await(); //阻塞
            System.out.println("end - condition wait");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock(); //释放锁
        }
    }
}
public class ConditionNotifyThread extends Thread {
    private Lock lock;
    private Condition condition;
    public ConditionNotifyThread(Lock lock, Condition condition) {
        this.lock = lock;
        this.condition = condition;
    }
    @Override
    public void run() {
        System.out.println("begin condition notify");
        try {
            lock.lock(); //获得锁
            condition.signalAll(); //唤醒
            System.out.println("end condition notify");
        } finally {
            lock.unlock(); //释放锁
        }
    }
}

通过condition实现一个简单的队列

public class ConditionBlockingQueueDemo {
    // 已经添加的元素数量
    private int size;
    // 容器
    private List<String> items;
    // 队列的大小
    private int count;

    private Lock lock = new ReentrantLock();
    // 让put方法阻塞
    private Condition putCondition = lock.newCondition();
    // 让take方法阻塞
    private Condition takeCondition = lock.newCondition();

    public ConditionBlockingQueueDemo(int count) {
        this.count = count;
        this.items = new ArrayList<>(count);
    }

    private void put(String item) throws InterruptedException {
        lock.lock();
        try {
            if (size >= count) {
                System.out.println("队列已满,阻塞等待");
                putCondition.await();
            }
            ++size; //增加元素个数
            this.items.add(item);
            takeCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    private String take() throws InterruptedException {
        lock.lock();
        try {
            if (size <= 0) {
                System.out.println("队列已经空了,不能取了,阻塞等待");
                takeCondition.await();
            }
            --size;
            String item = this.items.remove(0);
            putCondition.signal();
            return item;
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ConditionBlockingQueueDemo demo = new ConditionBlockingQueueDemo(10);
        Thread t1 = new Thread(()->{
            try {
                for (int i = 0; i < 1000; i++) {
                    String item = "item-" + (i + 1);
                    demo.put(item);
                    System.out.println("生产了一个元素: " + item);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t1.start();
        Thread.sleep(100);

        Thread t2 = new Thread(()->{
            try {
                for (;;) {
                    String item = demo.take();
                    System.out.println("消费了一个元素: " + item);
                    Thread.sleep(100);
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        t2.start();
    }
}

打印结果如下 

生产了一个元素: item-1
生产了一个元素: item-2
消费了一个元素: item-1
生产了一个元素: item-3
消费了一个元素: item-2
生产了一个元素: item-4
消费了一个元素: item-3
生产了一个元素: item-5
消费了一个元素: item-4
....

阻塞队列中的方法

添加元素

  • add --> 如果队列满了,抛出异常
  • offer --> true/false,添加成功返回true,否则返回false
  • put --> 如果队列满了,则一直阻塞
  • offer(timeout) --> 带有超时时间,如果队列满了,会阻塞timeout时长,超过阻塞时长,则返回false

移除元素

  • element --> 如果队列为空,抛出异常
  • peek --> true/false,移除成功返回true,否则返回false
  • take --> 如果队列为空,则一直阻塞
  • poll(timeout) --> 如果超时了还没返回元素,则返回null

 JUC包中的阻塞队列

  • ArrayBlockingQueue:基于数组结构
  • LinkedBlockingQueue:基于链表结构
  • PriorityBlockingQueue:基于优先级队列
  • DelayQueue:允许延时执行的队列
  • SynchronousQueue:没有任何存储结构的队列(在线程池Executors.newCachedThreadPool方法()里有用到)

2.4.7 ForkJoin

Fork/Join框架是一个Java7提供的一个用于并行执行任务的框架,把一个大任务拆分成若干个小任务,最终汇总每个小任务执行结果后得到大任务的结果,适用于数字求和. 通过双端队列和工作窃取算法实现。

Fork方法:让task任务拆分异步执行。

Join方法:让task任务同步执行,等待返回结果然后合并。

代码演示 

public class ForkJoinDemo extends RecursiveTask<Integer> {
    private final int THREHOLD = 200; //分割的阈值,每个任务的大小
    private int start;
    private int end;

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

    @Override
    protected Integer compute() {
        int sum = 0;
        boolean canSplit = (end - start) <= THREHOLD;
        if (canSplit) { // 可以分割
            int middle = (start + end) / 2;
            ForkJoinDemo leftTask = new ForkJoinDemo(start, middle);
            ForkJoinDemo rightTask = new ForkJoinDemo(middle + 1, end);
            leftTask.fork(); //此处会递归进入compute方法
            rightTask.fork(); //此处会递归进入compute方法
            Integer leftResult = leftTask.join();
            Integer rightResult = rightTask.join();
            sum = leftResult + rightResult;
        } else {
            for (int i = start; i <= end; i++) {
                sum += i;
            }
        }
        return sum;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        ForkJoinDemo forkJoinDemo = new ForkJoinDemo(1, 1000);
        ForkJoinTask<Integer> result = forkJoinPool.submit(forkJoinDemo);
        System.out.println(result.get());
    }
}

ForkJoin的实际应用

public class ItemA {
    private String name;
    private Integer num;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getNum() {
        return num;
    }
    public void setNum(Integer num) {
        this.num = num;
    }
}
public class ItemB {
    private String name;
    private Integer num;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public Integer getNum() {
        return num;
    }
    public void setNum(Integer num) {
        this.num = num;
    }
}
public class Context {
    private ItemA itemA;
    private ItemB itemB;
    public ItemA getItemA() {
        return itemA;
    }
    public void setItemA(ItemA itemA) {
        this.itemA = itemA;
    }
    public ItemB getItemB() {
        return itemB;
    }
    public void setItemB(ItemB itemB) {
        this.itemB = itemB;
    }
}
@Service
public class ItemAService extends AbstractLoadDataProcessor {
    @Override
    public void load(Context context) {
        ItemA itemA = new ItemA();
        itemA.setName("item A");
        itemA.setNum(100);
        context.setItemA(itemA);
    }
}
@Service
public class ItemBService extends AbstractLoadDataProcessor {
    @Override
    public void load(Context context) {
        ItemB itemB = new ItemB();
        itemB.setName("item B");
        itemB.setNum(200);
        context.setItemB(itemB);
    }
}
public interface ILoadDataProcessor {
    void load(Context context);
}
public abstract class AbstractLoadDataProcessor extends RecursiveAction implements ILoadDataProcessor {

    protected  Context context;

    @Override
    protected void compute() {
        load(context); //调用子类实现
    }

    public Context getContext() {
        this.join();
        return context;
    }

    public void setContext(Context context) {
        this.context = context;
    }
}

任务拆分和合并

@Service
public class ItemForkJoinDataProcessor extends AbstractLoadDataProcessor implements ApplicationContextAware {
    ApplicationContext applicationContext;
    private List<AbstractLoadDataProcessor> loadDataProcessors = new ArrayList<>();

    @Override
    public void load(Context context) {
        this.loadDataProcessors.forEach(abstractLoadDataProcessor -> {
            abstractLoadDataProcessor.setContext(this.context);
            abstractLoadDataProcessor.fork(); // 创建一个fork去拆分任务异步执行
        });
    }

    @Override
    public Context getContext() {
        this.loadDataProcessors.forEach(ForkJoinTask::join);
        return super.getContext();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
        loadDataProcessors.add(applicationContext.getBean(ItemAService.class));
        loadDataProcessors.add(applicationContext.getBean(ItemBService.class));
    }
}

入口调用

@RestController
public class IndexController {
    @Autowired
    ItemForkJoinDataProcessor itemForkJoinDataProcessor;
    @GetMapping("/index")
    public Context index() {
        Context context = new Context();
        itemForkJoinDataProcessor.setContext(context);
        ForkJoinPool forkJoinPool = new ForkJoinPool();
        forkJoinPool.submit(itemForkJoinDataProcessor);
        return itemForkJoinDataProcessor.getContext();
    }
}

2.4.8 ConcurrentHashMap

HashMap是线程非安全的,多线程下会存在线程安全问题

HashTable是线程安全的,因为在方法上有加synchronized同步锁,但是锁的粒度太大,会影响性能,所以引入了CHM.

ConcurrentHashMap在性能和安全方面做了平衡,在方法内部代码块上加了synchronized锁。

使用方法

computeIfAbsent:如果不存在则修改值
computeIfPresent:如果存在则修改值
compute:computeIfAbsent和computeIfPresent的结合
merge:数据合并

存储结构和实现

jdk 1.7:使用segment分段锁,锁的粒度大
jdk 1.8:使用链表 + 红黑树

ConcurrentHashMap和HashMap原理基本类似,只是在HashMap的基础上需要支持并发操作,保证多线程情况下对HashMap操作的安全性。当某个线程对集合内的元素进行数据操作时,会锁定这个元素,如果其他线程操作的数据hash得到相同的位置,就必须等到这个线程释放锁之后才能进行操作。

数据结构

  • 最外层是初始16位长度的数组,数据达到阈值(16 * 0.75)时会自动扩容(16 >> 1 = 32)
  • 插入数据时,先对key进行hash计算得到数据将要插入到数组的位置下标,如果此位置为空,则插入;
  • 如果此位置有数据,并且key相同,则替换做修改操作;
  • 如果此位置有数据,但key不同,则追加到此下标位置;
  • 初始情况下标位置是以单向链表结构存储数据,后续数据追加到链表尾部;
  • 当数组长度扩容到64,且某个位置链表长度达到8时,会将单向链表转换为红黑树结构
  • 做删除操作时,如果某个位置元素小于8时,会将红黑树转换为单向链表

扩容过程(满足两种情况会扩容):

  • 当新增节点后,所在位置链表元素个数达到阈值8,并且数组长度小于64;
  • 当增加集合元素后,当前数组内元素个数达到扩容阈值(16 * 0.75)时就会触发扩容;
  • 当线程处于扩容状态下,其他线程对集合进行操作时会参与帮助扩容;

默认是16位长度的数组,如果扩容就会新创建一个32位长度的数组,并对数据进行迁移,采用高低位迁移;

高低位迁移原理

扩容之后,数据迁移,有些数据需要迁移,有些数据不需要,低位不变,高位迁移;

数据扩容,但是计算存储位置下标的公式不变:i = (n - 1) & hash,所以有些key在扩容前后得到的下标位置相同,而有些key在扩容后hash得到的下标位置发生了改变;

假设:某个key的hash为9,数组长度为16,扩容到32,hash后得到的位置依然是9

假设:某个key,数组长度为16时hash值为4,而扩容为32长度时hash值变成了20

所以,table长度发生变化之后,获取同一个key在集合数组中的位置发生了变化,那么就需要迁移

链表转红黑树

当数组长度大于等于64,且某个数组位置的链表长度大于等于8,会把该位置链表转化为红黑树

原理

put插入元素

public V put(K key, V value) {
    return putVal(key, value, false); // 是否只有当不存在时才put
}
final V putVal(K key, V value, boolean onlyIfAbsent) {
    if (key == null || value == null) throw new NullPointerException();
    int hash = spread(key.hashCode());
    int binCount = 0;
    for (ConcurrentHashMap.Node<K,V>[] tab = table;;) {
        ConcurrentHashMap.Node<K,V> f; int n, i, fh;
        if (tab == null || (n = tab.length) == 0)
            tab = initTable();
        else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
            if (casTabAt(tab, i, null,
                    new ConcurrentHashMap.Node<K,V>(hash, key, value, null)))
                break;                   // no lock when adding to empty bin
        }
        else if ((fh = f.hash) == MOVED)
            tab = helpTransfer(tab, f);
        else {
            V oldVal = null;
            synchronized (f) {
                if (tabAt(tab, i) == f) {
                    if (fh >= 0) {
                        binCount = 1;
                        for (ConcurrentHashMap.Node<K,V> e = f;; ++binCount) {
                            K ek;
                            if (e.hash == hash &&
                                    ((ek = e.key) == key ||
                                            (ek != null && key.equals(ek)))) {
                                oldVal = e.val;
                                if (!onlyIfAbsent)
                                    e.val = value;
                                break;
                            }
                            ConcurrentHashMap.Node<K,V> pred = e;
                            if ((e = e.next) == null) {
                                pred.next = new ConcurrentHashMap.Node<K,V>(hash, key,
                                        value, null);
                                break;
                            }
                        }
                    }
                    else if (f instanceof ConcurrentHashMap.TreeBin) {
                        ConcurrentHashMap.Node<K,V> p;
                        binCount = 2;
                        if ((p = ((ConcurrentHashMap.TreeBin<K,V>)f).putTreeVal(hash, key,
                                value)) != null) {
                            oldVal = p.val;
                            if (!onlyIfAbsent)
                                p.val = value;
                        }
                    }
                }
            }
            if (binCount != 0) {
                if (binCount >= TREEIFY_THRESHOLD)
                    treeifyBin(tab, i);
                if (oldVal != null)
                    return oldVal;
                break;
            }
        }
    }
    addCount(1L, binCount);
    return null;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值