Java并发 结合源码分析AQS原理

前言:

如果说J.U.C包下的核心是什么?那我想答案只有一个就是AQS。那么AQS是什么呢?接下来让我们一起揭开AQS的神秘面纱

AQS是什么?

AQS是AbstractQueuedSynchronizer的简称。为什么说它是核心呢?是因为它提供了一个基于FIFO的队列和state变量来构建锁和其他同步装置的基础框架。下面是其底层的数据结构。

AQS的特点

1、其内使用Node实现FIFO(FirstInFirstOut)队列。可用于构建锁或者其他同步装置的基础框架

2、且利用了一个int类表示状态。在AQS中维护了一个volatile int state,通常表示有线程访问资源的状态,当state>1的时候表示线程重入的数量,主要有三个方法控制:getState(),setState(),CompareAndSetState()。后面的源码分析多用到这几个方法

3、使用方法是继承,子类通过继承并通过实现它的方法管理其状态(acquire和release)的方法操纵状态。

4、同时实现排它锁和共享锁模式。实际上AQS功能主要分为两类:独占(只有一个线程能执行)和共享(多个线程同时执行),它的子类要么使用独占功能要么使用共享功能,而ReentrantLock是通过两个内部类来实现独占和共享

CountDownLatch如何借助AQS实现计数功能?

先来说一下CountDownLatch,CountDownLatch是一个同步辅助类,通过它可以来完成类似阻塞当前线程的功能,即一个或多个线程一起等待,直到其他线程执行的操作完成。要实现上面的功能,CountDownLatch是通过一个给定的原子操作的计数器来实现。调用该类的await()方法的线程会一直处于阻塞状态,直到其他线程调用countDown()方法使得计数器的值变为0之后线程才会执行,这个计数器是不能被重置的。通常这个类会用在程序执行需要等待某个条件完成的场景,比如说并行计算,可将一个数据量很大的计算拆分成一个个子任务,当子任务完成之后,再将最终的结果汇总。每次访问CountDownLatch只能有一个线程,但是这个线程在使用完countDown()方法之后能多个线程能继续运行,而调用await()方法的线程就一定要计数器为0才会运行

下面来分析CountDownLatch的源码以及如何使用AQS框架

public class CountDownLatch {
  /**
   * CountDownLatch 实现同步控制
   * 底层是使用AQS的state来代表count
   */
  private static final class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 4982264981922014374L;
    //初始化内部类实际上是设置AQS的state
    Sync(int count) {
      setState(count);
    }

    int getCount() {
      return getState();
    }
    //尝试获取共享是看当前的state是否为0
    protected int tryAcquireShared(int acquires) {
      return (getState() == 0) ? 1 : -1;
    }
    /*尝试释放共享锁则是递减计数直到state==0就返回false代表资源已经释放完全否则就会使用CAS来让state减一*/
    protected boolean tryReleaseShared(int releases) {

      for (;;) {
        int c = getState();
        if (c == 0)
          return false;
        int nextc = c-1;
        if (compareAndSetState(c, nextc))
          return nextc == 0;
      }
    }
  }

  private final Sync sync;

  /**
   * 初始化CountDownLatch,实际上是初始化内部类,实际上是设置AQS的state,count不能小于0
   */
  public CountDownLatch(int count) {
    if (count < 0) throw new IllegalArgumentException("count < 0");
    this.sync = new Sync(count);
  }

  /**
   * 这里实际上是调用了AQS里的acquireSharedInterruptibly方法,完成的功能就是先去查看线程是否被中断,中断则抛出异常,没有被中断就会尝试获取共享资源。   * 注意在syn内部类中重写了tryAcquireShared,也就是当state为0就返回1,这时候就会将当前线程放入AQS的队列中去,也就是这时候线程可以不再阻塞而是尝试去获取锁
   */
  public void await() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
  }

  /**
   * 原理同上面方法,但是加了一个时间参数来设置等待的时间
   */
  public boolean await(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
  }

  /**
   * 这里传入参数为1,同样上面内部类一样重写了AQS的tryReleaseShared方法,使用这个重写的方法来让计数器原子操作的减一
   */
  public void countDown() {
    sync.releaseShared(1);
  }

  /**
   * 就是获取AQS的state
   */
  public long getCount() {
    return sync.getCount();
  }

  /**
   * 转换成字符串的方法
   */
  public String toString() {
    return super.toString() + "[Count = " + sync.getCount() + "]";
  }
}

由上面代码可看见CountDownLatch实现了AQS的共享锁,原理是操作state来实现计数,并且重写了tryAcquireShared(),tryReleaseShared()等方法

Semaphore是如何借助AQS实现控制并发访问线程个数?

Semaphore的功能类似于操作系统的信号量,可以很方便的控制某个资源同时被几个线程访问,即做并发访问控制,与CountDownLatch类似,同样是实现获取和释放两个方法。Semaphore的使用场景:常用于仅能提供访问的资源,比如数据库的连接数最大只有30,而应用程序的并发数可能远远大于30,这时候就可以使用Semaphore来控制同时访问的线程数。当Semaphore控制线程数到1的时候就和我们单线程一样了。同样Semaphore说是信号量的意思,我们这里就可以把它理解为十字路口的红绿灯,可以控制车流量(这里是控制线程数)

下面来分析Semaphore的源码以及如何使用AQS框

public class Semaphore implements java.io.Serializable {  private static final long serialVersionUID = -3222578661600680210L;  /** 所有机制都通过AbstractQueuedSynchronizer子类实现 */
  private final Sync sync;

  /**
   * 同样是通过内部类来实现AQS主要功能,使用state来表示许可证数量
   */
  abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = 1192457210091910933L;

    Sync(int permits) {
      setState(permits);
    }

    final int getPermits() {
      return getState();
    }
    /*
    * 不公平的获取方式,会有一个抢占锁的情况,即线程执行顺序会乱
    */
    final int nonfairTryAcquireShared(int acquires) {
      for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
          compareAndSetState(available, remaining))
          return remaining;
      }
    }
    /*
    * 释放资源
    */
    protected final boolean tryReleaseShared(int releases) {
      for (;;) {
        int current = getState();
        int next = current + releases;
        if (next < current) // overflow
          throw new Error("Maximum permit count exceeded");
        if (compareAndSetState(current, next))
          return true;
      }
    }

    final void reducePermits(int reductions) {
      for (;;) {
        int current = getState();
        int next = current - reductions;
        if (next > current) // underflow
          throw new Error("Permit count underflow");
        if (compareAndSetState(current, next))
          return;
      }
    }

    final int drainPermits() {
      for (;;) {
        int current = getState();
        if (current == 0 || compareAndSetState(current, 0))
          return current;
      }
    }
  }

  /**
   * 不公平的sync版本,使用的就是sync定义的不公平锁
   */
  static final class NonfairSync extends Sync {
    private static final long serialVersionUID = -2694183684443567898L;

    NonfairSync(int permits) {
      super(permits);
    }

    protected int tryAcquireShared(int acquires) {
      return nonfairTryAcquireShared(acquires);
    }
  }

  /**
   * 公平版本,获取锁的线程顺序就是线程启动的顺序。具体是使用hasQueuedPredecessors()方法判断“当前线程”是不是CLH队列中的第一个线程。   * 若不是的话,则返回-1,是就设置获取许可证,并检查许可证数量是否足够
   */
  static final class FairSync extends Sync {
    private static final long serialVersionUID = 2014338818796000944L;

    FairSync(int permits) {
      super(permits);
    }

    protected int tryAcquireShared(int acquires) {
      for (;;) {
        if (hasQueuedPredecessors())
          return -1;
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
          compareAndSetState(available, remaining))
          return remaining;
      }
    }
  }

  /**
   * 默认使用不公平的版本,如果需要公平的,则需要两个参数
   */
  public Semaphore(int permits) {
    sync = new NonfairSync(permits);
  }

  public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
  }

  /**
   * 分析同CountDownLatch中的类似方法,具体的实现都是内部类中的获取方法,这里是获取一个许可
   */
  public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
  }

  /**
   *功能同上,但是这里不会检测线程是否被中断
   */
  public void acquireUninterruptibly() {
    sync.acquireShared(1);
  }

  /**
   * 尝试获取
   */
  public boolean tryAcquire() {
    return sync.nonfairTryAcquireShared(1) >= 0;
  }

  /**
   * 在一段时间内一直尝试获取许可
   */
  public boolean tryAcquire(long timeout, TimeUnit unit)
    throws InterruptedException {
    return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
  }

  /**
   * 当前线程释放一个许可证
   */
  public void release() {
    sync.releaseShared(1);
  }

  /**
   * 可以规定一个线程获得许可证的数量
   */
  public void acquire(int permits) throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireSharedInterruptibly(permits);
  }

  public void acquireUninterruptibly(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.acquireShared(permits);
  }

  public boolean tryAcquire(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    return sync.nonfairTryAcquireShared(permits) >= 0;
  }

  public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
    throws InterruptedException {
    if (permits < 0) throw new IllegalArgumentException();
    return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
  }

  /**
   * 同样可以规定一个线程释放许可证的数量
   */
  public void release(int permits) {
    if (permits < 0) throw new IllegalArgumentException();
    sync.releaseShared(permits);
  }

  /**
   * 当前的许可还剩几个
   */
  public int availablePermits() {
    return sync.getPermits();
  }

  /**
   * 销毁所有许可
   */
  public int drainPermits() {
    return sync.drainPermits();
  }

  protected void reducePermits(int reduction) {
    if (reduction < 0) throw new IllegalArgumentException();
    sync.reducePermits(reduction);
  }

  public final boolean hasQueuedThreads() {
    return sync.hasQueuedThreads();
  }

  public final int getQueueLength() {
    return sync.getQueueLength();
  }

  protected Collection<Thread> getQueuedThreads() {
    return sync.getQueuedThreads();
  }
  public String toString() {
    return super.toString() + "[Permits = " + sync.getPermits() + "]";
  }}

上面对于Semaphore的一些重要内部类和常用方法进行了解释,与CountDownLatch很类似,实现的都是共享的功能,即Semaphore允许得到许可证的线程同时执行,而CountDownLatch允许调用countDown()方法的线程同时执行。并且都是通过内部类实现的。相信看到这里,你能越来越看见AQS为什么被称作JUC包的核心。下面就来介绍一下ReentrantLock

ReentrantLock是如何借助AQS实现锁机制

ReentrantLock是可重入锁,前面博客中写到synchronized实现的锁也是可重入的。不过synchronized是基于JVM指令实现,而ReentrantLock是使用Java代码实现的。ReentrantLock重点就是需要我们手动声明加锁和释放锁,如果手工忘记释放锁,很有可能就会导致死锁,即资源永远都被锁住,其他线程无法得到,当前线程也释放不出去。ReentrantLock实现的是自旋锁,通过循环调用CAS操作实现加锁,避免了线程进入内核态的阻塞状态,所以性能较好。ReentrantLock内部同样实现了公平锁和非公平锁。事实上Synchronized能做的ReentrantLock都能做,但是反过来就不一样了、

经过前面的源码分析我们发现核心的都在当前类的内部类里,而当前类的一些方法不过是使用的内部类以及AQS的方法罢了,所以下面我们就来分析ReentrantLock中的三个内部类。

public class ReentrantLock implements Lock, java.io.Serializable {
  private static final long serialVersionUID = 7373984872572414699L;
  /** 同步的机制都是通过内部类来实现的 */
  private final Sync sync;

  /**
   * 在ReentrantLock中state表示的是线程重入锁的次数,当state为0时才能释放锁
   */
  abstract static class Sync extends AbstractQueuedSynchronizer {
    private static final long serialVersionUID = -5179523762034025860L;

    /**
     * 这个抽象方法提供给公平锁和不公平锁来单独实现,父类不实现
     */
    abstract void lock();

    /**
     * 首先得到当前线程,而后获取state,如果state为0,也就是没有线程获得当前锁,那么就设置当前线程拥有当前锁的独占访问权,并且返回true。     * 如果state不为0,那么就看当前线程是否是已经获得过锁的线程,如果是就让state+=acquire,acquire一般是1,即表示线程重入并且返回true。     * 上面两个条件都不满足就代表是锁被其他线程获取了,当前线程获取不到,所以返回false
     */
    final boolean nonfairTryAcquire(int acquires) {
      final Thread current = Thread.currentThread();
      int c = getState();
      if (c == 0) {
        if (compareAndSetState(0, acquires)) {
          setExclusiveOwnerThread(current);
          return true;
        }
      }
      else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0) // overflow
          throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
      }
      return false;
    }
    /**
     * 先判断当前线程等不等于拥有锁的线程,不等于就会抛异常,也就是释放不了。     * 等于之后就看state-releases是否为0,当为0的时候就代表释放完全。     * 可以设置锁的状态为没有线程拥有,从而让锁能被其他线程竞争,否则就设置state,代表线程重入该锁,并且线程还没释放完全。
     */
    protected final boolean tryRelease(int releases) {
      int c = getState() - releases;
      if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException();
      boolean free = false;
      if (c == 0) {
        free = true;
        setExclusiveOwnerThread(null);
      }
      setState(c);
      return free;
    }
    /*
     *该方法检验当前线程是否是锁的独占者
     */
    protected final boolean isHeldExclusively() {
      return getExclusiveOwnerThread() == Thread.currentThread();
    }
    /*
     *该方法是创建一个条件锁,本文不做具体分析
     */
    final ConditionObject newCondition() {
      return new ConditionObject();
    }

    // Methods relayed from outer class

    final Thread getOwner() {
      return getState() == 0 ? null : getExclusiveOwnerThread();
    }

    final int getHoldCount() {
      return isHeldExclusively() ? getState() : 0;
    }

    final boolean isLocked() {
      return getState() != 0;
    }

    /**
     * 使得该类从流中能重构实例,并且会重置为解锁状态
     */
    private void readObject(java.io.ObjectInputStream s)
      throws java.io.IOException, ClassNotFoundException {
      s.defaultReadObject();
      setState(0);
    }
  }

  /**
   * Sync 的不公平版本
   */
  static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * 将state从0更新到1成功的话就让当前线程获取锁,否则就会尝试获得锁和获取当前节点的前一节点,并判断这一个节点是否为头节点,即当前线程是不是头节点的直接后继。     * 如果两个中有一个失败则线程中断,进入阻塞状态
     */
    final void lock() {
      if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
      else
        acquire(1);
    }

    protected final boolean tryAcquire(int acquires) {
      return nonfairTryAcquire(acquires);
    }
  }

  /**
   * Sync 的公平版本
   */
  static final class FairSync extends Sync {
    private static final long serialVersionUID = -3000897897090466540L;
    /**
    * 尝试获得锁和获取当前节点的前一节点,并判断这一个节点是否为头节点,即当前线程是不是头节点的直接后继,如果两个中有一个失败则线程中断,进入阻塞状态。    * 也就是一定按照队列中线程的顺序来实现
    */
    final void lock() {
      acquire(1);
    }

    /**
     * 跟不公平的版本相比其实是在state为0的时候检查当前线程是不是在队列的头部节点的直接后继,来达到公平的概念
     */
    protected final boolean tryAcquire(int acquires) {
      final Thread current = Thread.currentThread();
      int c = getState();
      if (c == 0) {
        if (!hasQueuedPredecessors() &&
          compareAndSetState(0, acquires)) {
          setExclusiveOwnerThread(current);
          return true;
        }
      }
      else if (current == getExclusiveOwnerThread()) {
        int nextc = c + acquires;
        if (nextc < 0)
          throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
      }
      return false;
    }
  }
}

ReentrantLock和上面两个类最不同的莫过于ReentrantLock使用的是独占功能,即每次只能有一个线程来获取ReentrantLock类。ReentrantLock类下还有很多方法,这里就不一一介绍,但是本质都是内部类中的实现以及AQS的一些调用

总结:

AQS只是一个基础的框架,里面最核心的就是维护了state变量和CHL队列,而其他的类全部都是通过继承的方法进行扩展,虽然没有直接说源码,但是通过上面三个主要类的源码分析再去看AQS已经不是难事。继承主要改变的就是获取和释放的方法,通过这两个方法来对state和队列进行操作达到我们能够进行的并发控制的功能,事实上J.U.C包下的类和能够实现的功能远不止这三个,后面会选择重点的来介绍。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • Java并发系列之AbstractQueuedSynchronizer源码分析(概要分析)

    学习Java并发编程不得不去了解一下java.util.concurrent这个包,这个包下面有许多我们经常用到的并发工具类,例如:ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore等.而这些类的底层实现都依赖于AbstractQueuedSynchronizer这个类,由此可见这个类的重要性.所以在Java并发系列文章中我首先对AbstractQueuedSynchronizer这个类进行分析,由于这个类比较重要,而且代码比较长,为了

  • Java并发系列之CountDownLatch源码分析

    CountDownLatch(闭锁)是一个很有用的工具类,利用它我们可以拦截一个或多个线程使其在某个条件成熟后再执行.它的内部提供了一个计数器,在构造闭锁时必须指定计数器的初始值,且计数器的初始值必须大于0.另外它还提供了一个countDown方法来操作计数器的值,每调用一次countDown方法计数器都会减1,直到计数器的值减为0时就代表条件已成熟,所有因调用await方法而阻塞的线程都会被唤醒.这就是CountDownLatch的内部机制,看起来很简单,无非就是阻塞一部分线程让其在达到某个条

  • Java并发编程学习之Unsafe类与LockSupport类源码详析

    一.Unsafe类的源码分析 JDK的rt.jar包中的Unsafe类提供了硬件级别的原子操作,Unsafe里面的方法都是native方法,通过使用JNI的方式来访问本地C++实现库. rt.jar 中 Unsafe 类主要函数讲解, Unsafe 类提供了硬件级别的原子操作,可以安全的直接操作内存变量,其在 JUC 源码中被广泛的使用,了解其原理为研究 JUC 源码奠定了基础. 首先我们先了解Unsafe类中主要方法的使用,如下: 1.long objectFieldOffset(Field

  • Java并发系列之ReentrantLock源码分析

    在Java5.0之前,协调对共享对象的访问可以使用的机制只有synchronized和volatile.我们知道synchronized关键字实现了内置锁,而volatile关键字保证了多线程的内存可见性.在大多数情况下,这些机制都能很好地完成工作,但却无法实现一些更高级的功能,例如,无法中断一个正在等待获取锁的线程,无法实现限定时间的获取锁机制,无法实现非阻塞结构的加锁规则等.而这些更灵活的加锁机制通常都能够提供更好的活跃性或性能.因此,在Java5.0中增加了一种新的机制:Reentrant

  • java并发容器CopyOnWriteArrayList实现原理及源码分析

    CopyOnWriteArrayList是Java并发包中提供的一个并发容器,它是个线程安全且读操作无锁的ArrayList,写操作则通过创建底层数组的新副本来实现,是一种读写分离的并发策略,我们也可以称这种容器为"写时复制器",Java并发包中类似的容器还有CopyOnWriteSet.本文会对CopyOnWriteArrayList的实现原理及源码进行分析. 实现原理 我们都知道,集合框架中的ArrayList是非线程安全的,Vector虽是线程安全的,但由于简单粗暴的锁同步机制,

  • 简谈java并发FutureTask的实现

    概述 在使用java多线程解决问题的时候,为了提高效率,我们常常会异步处理一些计算任务并在最后异步的获取计算结果,这个过程的实现离不开Future接口及其实现类FutureTask.FutureTask类实现了Runnable, Future接口,接下来我会通过源码对该类的实现进行详解. 使用 我们先看下FutureTask中的主要方法如下,可以看出FutureTask实现了任务及异步结果的集合功能.看到这块的方法,大家肯定会有疑问,Runnable任务的run方法返回空,FutureTask如

  • 浅谈Java并发 J.U.C之AQS:CLH同步队列

    CLH同步队列是一个FIFO双向队列,AQS依赖它来完成同步状态的管理,当前线程如果获取同步状态失败时,AQS则会将当前线程已经等待状态等信息构造成一个节点(Node)并将其加入到CLH同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点唤醒(公平锁),使其再次尝试获取同步状态. 在CLH同步队列中,一个节点表示一个线程,它保存着线程的引用(thread).状态(waitStatus).前驱节点(prev).后继节点(next),其定义如下: static final class Node

  • Java并发 结合源码分析AQS原理

    前言: 如果说J.U.C包下的核心是什么?那我想答案只有一个就是AQS.那么AQS是什么呢?接下来让我们一起揭开AQS的神秘面纱 AQS是什么? AQS是AbstractQueuedSynchronizer的简称.为什么说它是核心呢?是因为它提供了一个基于FIFO的队列和state变量来构建锁和其他同步装置的基础框架.下面是其底层的数据结构. AQS的特点 1.其内使用Node实现FIFO(FirstInFirstOut)队列.可用于构建锁或者其他同步装置的基础框架 2.且利用了一个int类表示

  • Java并发LinkedBlockingQueue源码分析

    目录 简介 常量 构造方法 put await isOnSyncQueue signal 简介 LinkedBlockingQueue是一个阻塞的有界队列,底层是通过一个个的Node节点形成的链表实现的,链表队列中的头节点是一个空的Node节点,在多线程下操作时会使用ReentrantLock锁来保证数据的安全性,并使用ReentrantLock下的Condition对象来阻塞以及唤醒线程. 常量 /** * 链表中的节点类 */ static class Node<E> { //节点中的元素

  • Java并发Timer源码分析

    timer在JDK里面,是很早的一个API了.具有延时的,并具有周期性的任务,在newScheduledThreadPool出来之前我们一般会用Timer和TimerTask来做,但是Timer存在一些缺陷,为什么这么说呢? Timer只创建唯一的线程来执行所有Timer任务.如果一个timer任务的执行很耗时,会导致其他TimerTask的时效准确性出问题.例如一个TimerTask每10秒执行一次,而另外一个TimerTask每40ms执行一次,重复出现的任务会在后来的任务完成后快速连续的被

  • Java 读写锁源码分析

    前言 在实际项目中,比如我们有一个共享资源文件,我们程序会会同时并发的去读.写这个共享资源文件,那怎么能保证在高并发场景下安全.高效读写呢?OK,看了下文便知 提示:以下是本篇文章正文内容,案例仅供参考 一.技术介绍 1.ReentranReadWriteLock是什么? ReadWriteLock提供了readLock和writeLock两种锁的操作机制,一个是读锁,一个是写锁,而它的实现类就是ReentranReadWriteLock 读锁可以在没有写锁的时候被多个线程同时持有,写锁是独占的

  • Java集合框架源码分析之LinkedHashMap详解

    LinkedHashMap简介 LinkedHashMap是HashMap的子类,与HashMap有着同样的存储结构,但它加入了一个双向链表的头结点,将所有put到LinkedHashmap的节点一一串成了一个双向循环链表,因此它保留了节点插入的顺序,可以使节点的输出顺序与输入顺序相同. LinkedHashMap可以用来实现LRU算法(这会在下面的源码中进行分析). LinkedHashMap同样是非线程安全的,只在单线程环境下使用. LinkedHashMap源码剖析 LinkedHashM

  • Java struts2请求源码分析案例详解

    Struts2是Struts社区和WebWork社区的共同成果,我们甚至可以说,Struts2是WebWork的升级版,他采用的正是WebWork的核心,所以,Struts2并不是一个不成熟的产品,相反,构建在WebWork基础之上的Struts2是一个运行稳定.性能优异.设计成熟的WEB框架. 我这里的struts2源码是从官网下载的一个最新的struts-2.3.15.1-src.zip,将其解压即可.里面的目录页文件非常的多,我们只需要定位到struts-2.3.15.1\src\core

  • Java Array.sort()源码分析讲解

    阅读起点: Arrays.sort(nums1); 使用ctrl+左键进入sort()方法 1.Arrays.sort() 关于sort()的方法一共有14个,就目前调用的来看是以下这种最基础的. public static void sort(int[] a) { DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0); } 2.DualPivotQuicksort DualPivotQuicksort即双轴快排,定义了七种原始类型的排序

  • Java BufferedWriter BufferedReader 源码分析

    一:BufferedWriter 1.类功能简介: BufferedWriter.缓存字符输出流.他的功能是为传入的底层字符输出流提供缓存功能.同样当使用底层字符输出流向目的地中写入字符或者字符数组时.每写入一次就要打开一次到目的地的连接.这样频繁的访问不断效率底下.也有可能会对存储介质造成一定的破坏.比如当我们向磁盘中不断的写入字节时.夸张一点.将一个非常大单位是G的字节数据写入到磁盘的指定文件中的.没写入一个字节就要打开一次到这个磁盘的通道.这个结果无疑是恐怖的.而当我们使用Buffered

  • java.util.Collection源码分析与深度理解

    写在开头 java.util.Collection 作为Java开发最常用的接口之一,我们经常使用,今天我带大家一起研究一下Collection接口,希望对大家以后的编程以及系统设计能有所帮助,本文所研究的jdk版本为jdk1.8.0_131 明确一下几点: Collection是接口,其继承了Iterable接口 Collection属于单值类型集合,重点子接口List接口和Set接口 Java.util.List接口(有序.不唯一) ArraryList ArrayList 是一个数组队列,

  • Java集合系列之LinkedHashMap源码分析

    这篇文章我们开始分析LinkedHashMap的源码,LinkedHashMap继承了HashMap,也就是说LinkedHashMap是在HashMap的基础上扩展而来的,因此在看LinkedHashMap源码之前,读者有必要先去了解HashMap的源码,可以查看我上一篇文章的介绍<Java集合系列[3]----HashMap源码分析>.只要深入理解了HashMap的实现原理,回过头来再去看LinkedHashMap,HashSet和LinkedHashSet的源码那都是非常简单的.因此,读

随机推荐