diff --git a/week_01/07/LinkedList-007.md b/week_01/07/LinkedList-007.md new file mode 100644 index 0000000000000000000000000000000000000000..6eecf628bf30ddc2bb6f7bc5eb0207c235b4c3ed --- /dev/null +++ b/week_01/07/LinkedList-007.md @@ -0,0 +1,267 @@ +LinkedList源码分析 +前言:源码都是基于JDK1.8,后续的版本都是基于1.8,就另外不做说明了。LinkdedList在开发也是比较常用的一个类,这里来看看链表是怎样实现的 +1.定义的成员变量分析 + transient int size = 0; //初始化大小,默认为0,不能被序列化 + transient Node first; //第一个节点,判断一个节点是否是第一个节点的条件:(first == null && last == null) || (first.prev == null && first.item != null) 源码注释上的,不是我意淫的 + transient Node last; //最后一个节点,判断是否是最后一个节点的条件: (first == null && last == null) || (last.next == null && last.item != null) +2.构造方法分析 + //构造一个空的list + public LinkedList() { + } + //带集合参数的构造方法 + public LinkedList(Collection c) { + this(); //调用默认的构造方法 + addAll(c); //把集合所有元素添加进去,这个方法后面分析 + } +3.分析添加、删除和查询方法之前,先分析一下LinkedList存储的结构,方便后面理解 + //在LinkedList里面声明了一个静态内部内Node + private static class Node { + E item; //链表实际存放的元素 + Node next; //指向下一个元素 + Node prev; //指向上一个元素 + //构造函数 + Node(Node prev, E element, Node next) { + this.item = element; + this.next = next; + this.prev = prev; + } + } +4.老规矩,分析几个重要的方法 + //add方法有两个一个返回是否添加成功,一个在指定位置添加 + public boolean add(E e) { + linkLast(e); + return true; //添加成功返回true + } + //添加元素到最后 + void linkLast(E e) { + final Node l = last; //把之前最后的元素赋值给l + final Node newNode = new Node<>(l, e, null); //new一个节点元素,把元素的prev指向l + last = newNode; //指定新new的元素为最后一个元素 + if (l == null) //如果l为空,说明之前集合中没有元素,赋值给first + first = newNode; + else //把l.next指向新添加的节点 + l.next = newNode; + size++; //集合大小+1 + modCount++; //操作次数+1 + } + //在指定位置添加元素 + public void add(int index, E element) { + checkPositionIndex(index); + if (index == size) //如果要添加的位置和size相等,添加到最后 + linkLast(element); + else //不相等则添加到指定位置 + linkBefore(element, node(index)); + } + //判断指定位置是否在0-size之间,不在抛出异常 + private void checkPositionIndex(int index) { + if (!isPositionIndex(index)) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + private boolean isPositionIndex(int index) { + return index >= 0 && index <= size; + } + //添加元素到指定节点之前 + void linkBefore(E e, Node succ) { + // assert succ != null; + final Node pred = succ.prev; //节点的前一个节点 + final Node newNode = new Node<>(pred, e, succ); //new一个节点,把前一个节点pred和后一个节点succ指定好 + succ.prev = newNode; //把之前节点的前一个节点指向newNode + if (pred == null) //如果pred为空说明succ之前没有元素,把first元素指定为newNode + first = newNode; + else //如果有元素,把pred.next指向newNode + pred.next = newNode; + size++; //size+1 + modCount++; //操作次数+1 + } + //addFirst方法 + public void addFirst(E e) { + linkFirst(e); + } + //添加一个元素到第一个节点 + private void linkFirst(E e) { + final Node f = first; //第一个节点 + final Node newNode = new Node<>(null, e, f); //new一个新节点 + first = newNode; //把新节点赋值给first + if (f == null) //如果f为空,说明之前没有元素,所以把最后一个节点也赋值为新节点 + last = newNode; + else //如果不为空,把f.prev指向newNode + f.prev = newNode; + size++; + modCount++; + } + //addLast方法 + public void addLast(E e) { + linkLast(e); //这个方法上面已经分析过了,这里就不要分析了 + } + //addAll方法 + public boolean addAll(Collection c) { + return addAll(size, c); + } + public boolean addAll(int index, Collection c) { + checkPositionIndex(index); //判断index是否在0-size之间 + Object[] a = c.toArray(); //把集合转换为Object[]数组 + int numNew = a.length; //得到要插入数组的长度 + if (numNew == 0) //如果长度为0,说明是个空数组,直接返回false + return false; + Node pred, succ; //定义这两个变量的原因是因为要插入的集合,要插入这两个元素中间 + if (index == size) { //index和size相等,说明加到最后 + succ = null; //最后的节点为空 + pred = last; //pred为最后的节点 + } else { //如果不相等,succ为index位置的节点 + succ = node(index); + pred = succ.prev; //pred为succ前的一个节点,这两个元素中间为要插入集合的位置 + } + for (Object o : a) { //循环要插入的数组 + @SuppressWarnings("unchecked") E e = (E) o; //强转为E类型,就是用的时候自己定义的类型 + Node newNode = new Node<>(pred, e, null); //new一个节点,前一个节点为pred,后一个节点为null + if (pred == null) //如果pred为空,说明之前集合为null + first = newNode; //first为新new的节点 + else //如果pred不为空 + pred.next = newNode; //pred.next为新new的节点 + pred = newNode; //新new的节点作为pred节点,继续往后面添加元素 + } + if (succ == null) { //如果succ为空,说明添加到最后 + last = pred; //上面循环添加的最后一个元素就是last + } else { //如果不是添加到最后 + pred.next = succ; //把pred.next指向succ + succ.prev = pred; //把succ.prev指向pred + } + size += numNew; //集合大小+numNew + modCount++; //修改次数+1 + return true; //返回true + } + //get方法 + public E get(int index) { + checkElementIndex(index); //检查是否越界 + return node(index).item; //取出节点的元素 + } + //判断index是否在[0-size)中,注意不包括size,不在抛出异常 + private void checkElementIndex(int index) { + if (!isElementIndex(index)) + throw new IndexOutOfBoundsException(outOfBoundsMsg(index)); + } + private boolean isElementIndex(int index) { + return index >= 0 && index < size; + } + Node node(int index) { + // assert isElementIndex(index); + //从集合中间开始找,size >> 1 等价于 size / 2,如果index小于集合的一半,从头开始找 + if (index < (size >> 1)) { + Node x = first; + for (int i = 0; i < index; i++) + x = x.next; + return x; + } else { //如果index大于集合的一半,从最后开始找 + Node x = last; + for (int i = size - 1; i > index; i--) + x = x.prev; + return x; + } + } + //getFirst方法 + public E getFirst() { + final Node f = first; //取第一个节点,然后返回第一个节点的值 + if (f == null) + throw new NoSuchElementException(); + return f.item; + } + //getLast方法 + public E getLast() { + final Node l = last; //取最后一个节点,返回最后一个节点的值 + if (l == null) + throw new NoSuchElementException(); + return l.item; + } + //remove删除方法 + public boolean remove(Object o) { + if (o == null) { //如果删除的元素为空 + for (Node x = first; x != null; x = x.next) { + if (x.item == null) { //如果找到为空的元素,就删除 + unlink(x); + return true; + } + } + } else { //如果删除的元素不为空 + for (Node x = first; x != null; x = x.next) { + if (o.equals(x.item)) { //如果找到了相同的元素,删除 + unlink(x); + return true; + } + } + } + return false; + } + E unlink(Node x) { + // assert x != null; + final E element = x.item; + final Node next = x.next; + final Node prev = x.prev; + if (prev == null) { //要删除的元素没有prev元素,说明是第一个节点 + first = next; //这个节点删除后,第一个节点就就成了下一个了 + } else { //如果删除的不是第一个节点 + prev.next = next; //那删除节点的前一个节点的下一个节点等于删除节点的下一个节点 + x.prev = null; //要删除的节点的前一个节点赋值为空 + } + if (next == null) { //要删除的元素没有next元素,说明是最后一个节点 + last = prev; //这个节点删除后,上一个节点就变成了最后一个了 + } else { //如果删除的不是最后一个节点 + next.prev = prev; //要删除节点的下一个节点的上一个节点等于删除节点的上一个节点 + x.next = null; //要删除的节点的下一个节点赋值为空 + } + x.item = null; //把元素赋值为空 + size--; //size大小-1 + modCount++; //操作次数-1 + return element; + } + //removeFirst方法 + public E removeFirst() { + final Node f = first; //取第一个元素 + if (f == null) + throw new NoSuchElementException(); + return unlinkFirst(f); + } + private E unlinkFirst(Node f) { + // assert f == first && f != null; + final E element = f.item; + final Node next = f.next; //第一个节点的下一个节点 + f.item = null; //把元素清空 + f.next = null; // help GC + first = next; //把下一个节点等于第一个节点 + if (next == null) //如果没有下一个节点,说明只有一个节点 + last = null; //把唯一的一个节点删除了,last就为空了 + else //如果不止一个节点 + next.prev = null; //把下一个节点的上一个节点赋值null + size--; + modCount++; + return element; + } + //removeLast方法 + public E removeLast() { + final Node l = last; //取最后一个元素 + if (l == null) + throw new NoSuchElementException(); + return unlinkLast(l); + } + private E unlinkLast(Node l) { + // assert l == last && l != null; + final E element = l.item; + final Node prev = l.prev; //最后一个节点的上一个节点 + l.item = null; //把元素清空 + l.prev = null; // help GC + last = prev; //把上一个节点等于最后一个节点 + if (prev == null) //如果没有上一个节点,说明只有一个节点 + first = null; //把first赋值为空 + else //如果不止一个节点 + prev.next = null; //把上一个节点的下一个节点赋值null + size--; + modCount++; + return element; + } +5.总结:LinkedList就分析到这,如有错误,欢迎指正 + + + + + + + diff --git a/week_02/07/AtomicInteger-007.md b/week_02/07/AtomicInteger-007.md new file mode 100644 index 0000000000000000000000000000000000000000..bf53a8793f03702e37e67ecae58b3424fd04c23d --- /dev/null +++ b/week_02/07/AtomicInteger-007.md @@ -0,0 +1,57 @@ +AtomicInteger源码分析 +前言:AtomicInteger是int类型的原子操作类,底层基于Unsafe类来实现原子操作 +1.成员变量分析 + private static final Unsafe unsafe = Unsafe.getUnsafe(); //声明Unsafe类实例 + private static final long valueOffset; //偏移量,没太搞清楚是干嘛使的 + //这是给valueOffset赋值的,基于Unsafe类中的方法 + static { + try { + valueOffset = unsafe.objectFieldOffset + (AtomicInteger.class.getDeclaredField("value")); + } catch (Exception ex) { throw new Error(ex); } + } + private volatile int value; //int类型的值,保证可见性,即一个线程修改了这个值,对另一个线程是可见的 +2.构造方法分析 + //指定初始值构造方法 + public AtomicInteger(int initialValue) { + value = initialValue; + } + //默认的构造方法 + public AtomicInteger() { + } + +3.这里介绍几个典型的方法 + //compareAndSet方法,比较和赋值 + public final boolean compareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } + //这个方法是native方法,java中看不到,不过根据getAndSet方法来看,应该是借助偏移量来查看原始值和期望值是否一样,如果一样,就更改为新值,基于CAS+自旋实现 + public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); + public final int getAndSet(int newValue) { + return unsafe.getAndSetInt(this, valueOffset, newValue); + } + public final int getAndSetInt(Object var1, long var2, int var4) { + int var5; + do { + var5 = this.getIntVolatile(var1, var2); + } while(!this.compareAndSwapInt(var1, var2, var5, var4)); + return var5; + } + //这也是通过Unsafe类里的方法来实现,跟上面的一样 + public final boolean compareAndSet(int expect, int update) { + return unsafe.compareAndSwapInt(this, valueOffset, expect, update); + } +4.总结:除此之外还有些加减的方法,底层也差不多,就不一一介绍了。如果有其它可以补充的,欢迎指出 + + + + + + + + + + + + + diff --git a/week_02/07/AtomicStampedReference-007.md b/week_02/07/AtomicStampedReference-007.md new file mode 100644 index 0000000000000000000000000000000000000000..c9e3da81a3d4da2fa2f7bc5f8aeee102781781ec --- /dev/null +++ b/week_02/07/AtomicStampedReference-007.md @@ -0,0 +1,55 @@ +AtomicStampedReference源码分析 +前言:AtomicStampedReference类为了解决CAS带来的ABA问题,下面一起来看一看怎么解决ABA问题 +1.简单介绍一下什么是ABA问题,在多线程环境中,一个线程去取同一块内存地址的值两次,两次得到一样的值,就会认为这个值没有被修改过,当然有可能另一个线程会在这个线程两次读取之间把这个内存地址的值从A修改成B,然后又修改成A,这时候就会出现ABA问题。如何解决ABA问题呢?下面就来看看源码里面是怎么实现的吧。 +2.首先里面申明了一个静态内部类 + private static class Pair { + final T reference; //实际存在的数据 + final int stamp; //类似于版本号,邮戳 + private Pair(T reference, int stamp) { + this.reference = reference; + this.stamp = stamp; + } + static Pair of(T reference, int stamp) { + return new Pair(reference, stamp); + } + } + private volatile Pair pair; //可见性的pair + private static final sun.misc.Unsafe UNSAFE = sun.misc.Unsafe.getUnsafe(); //声明UNSAFE实例 + private static final long pairOffset = + objectFieldOffset(UNSAFE, "pair", AtomicStampedReference.class); //获取pair的偏移量,相当于在内存的地址 +3.构造方法 + //传入初始值和版本号 + public AtomicStampedReference(V initialRef, int initialStamp) { + pair = Pair.of(initialRef, initialStamp); + } +4.compareAndSet方法 + public boolean compareAndSet(V expectedReference, + V newReference, + int expectedStamp, + int newStamp) { + Pair current = pair; //当前值 + return + expectedReference == current.reference && //如果引用没变,相当于值没变 + expectedStamp == current.stamp && //版本号也没 + ((newReference == current.reference && //新引用等于旧引用 + newStamp == current.stamp) || //新版本号等于旧版本 + casPair(current, Pair.of(newReference, newStamp))); //创建新的Pair对象 + } + //调用Unsafe里面的对比交换方法 + private boolean casPair(Pair cmp, Pair val) { + return UNSAFE.compareAndSwapObject(this, pairOffset, cmp, val); + } +5.总结:上面是Java里面解决ABA问题的源码,如有问题,欢迎指出 + + + + + + + + + + + + + diff --git a/week_02/07/LongAdder.md b/week_02/07/LongAdder.md new file mode 100644 index 0000000000000000000000000000000000000000..d8778633cbcc2ca6c49e3b72ef8fecad1a9bf92f --- /dev/null +++ b/week_02/07/LongAdder.md @@ -0,0 +1,60 @@ +LongAdder源码分析 +前言:LongAdder是1.8新添加的一个类,继承至Striped64类,而这个类又继承自Number类 +1.先看看Striped64这个类,看一下这个类里面定义的东西 + //CPU的核数 + static final int NCPU = Runtime.getRuntime().availableProcessors(); + transient volatile Cell[] cells; //LongAdder类里面主要操作的是Cell + transient volatile long base; //基本的数字 + transient volatile int cellsBusy; //标识Cell是否需要扩容 + @sun.misc.Contended static final class Cell { + volatile long value; //单元格要个性的值 + Cell(long x) { value = x; } + final boolean cas(long cmp, long val) { //CAS更新value的值 + return UNSAFE.compareAndSwapLong(this, valueOffset, cmp, val); + } + + // Unsafe mechanics + private static final sun.misc.Unsafe UNSAFE; + private static final long valueOffset; //偏移量 + static { + try { + UNSAFE = sun.misc.Unsafe.getUnsafe(); + Class ak = Cell.class; + //通过Unsafe得到偏移量的值 + valueOffset = UNSAFE.objectFieldOffset + (ak.getDeclaredField("value")); + } catch (Exception e) { + throw new Error(e); + } + } + } + 这里还有两个方法longAccumulate、doubleAccumulate暂时还没看懂,大意就是在多线程环境下,执行数量相加 +2.LongAdder类里面重要的两个方法,add和sum + //累加 + public void add(long x) { + Cell[] as; long b, v; int m; Cell a; + if ((as = cells) != null || !casBase(b = base, b + x)) { + boolean uncontended = true; + if (as == null || (m = as.length - 1) < 0 || + (a = as[getProbe() & m]) == null || + !(uncontended = a.cas(v = a.value, v + x))) + longAccumulate(x, null, uncontended); + } + } + //求得累加之后的值 + public long sum() { + Cell[] as = cells; Cell a; + long sum = base; + if (as != null) { + for (int i = 0; i < as.length; ++i) { + if ((a = as[i]) != null) + sum += a.value; + } + } + return sum; + } +3.总结:等后续研究过后,再写写这两个类里面的一些方法,如有问题,欢迎指出 + + + + diff --git a/week_02/07/Unsafe-007.md b/week_02/07/Unsafe-007.md new file mode 100644 index 0000000000000000000000000000000000000000..41956387967aa6c618159c9d649a4f1ea36c50e8 --- /dev/null +++ b/week_02/07/Unsafe-007.md @@ -0,0 +1,53 @@ +Unsafe源码分析 +前言:从这个类的名字来看,理解为不安全的类,为什么叫不安全的类,因为这个类可以去直接申请内存,以及一些底层JVM操作,所以,定义为Unsafe。Unsafe类中有很多方法,大多数是native的方法,基于c/c++实现的,看不到实现,所以这里只说几个操作类型的方法。方法大致可以分为8大类:CAS操作、内存操作、线程调度、数组相关、对象相关操作、Class相关操作、内存屏障、系统相关。 +1.Unsafe类被final修饰,是个不可变的类,不能被继承 + public final class Unsafe { + private static final Unsafe theUnsafe; //theUnsafe成员变量 + private static native void registerNatives(); //native注册方法 + private Unsafe() { //私有的构造函数 + } + @CallerSensitive //在有Reflection.getCallerClass()调用的时候,一般会有这个注解,说是为了防止通过反射获取最大的权限而设计,具体的还没研究清楚,如果有知道的,可以讲解一下 + //得到Unsafe实例,但是在开发中不能通过这个方法获取实例,会抛出异常 + public static Unsafe getUnsafe() { + Class var0 = Reflection.getCallerClass(); + if (!VM.isSystemDomainLoader(var0.getClassLoader())) { + throw new SecurityException("Unsafe"); + } else { + return theUnsafe; + } + } + static { //new Unsafe对象,以及一些注册等其它操作 + registerNatives(); + Reflection.registerMethodsToFilter(Unsafe.class, new String[]{"getUnsafe"}); + theUnsafe = new Unsafe(); + //还有一些其它赋值操作 + } + } + //那我们要用的话,如何实例化Unsafe类呢,两种方法,一种是把开发的jar包指定为扩展类加载器加载,第二种是通过反射来创建,明显是第二种实用。 + //Field field = Unsafe.class.getDeclaredField("theUnsafe"); + //field.setAccessible(true); + //Unsafe unsafe = (Unsafe)field.get(null); +2.cas操作相关方法 + public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5); + //以介绍compareAndSwapInt方法为例,其它两个方法也是一样的,判断期望值是否是等于原值,如果是的话,就修改成新的值 + public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); + + public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6); +3.内存操作 + //分配内存,注意这里是JVM之外的内存,不受JVM管理的,所以有内存泄漏的问题 + public native long allocateMemory(long var1); + public native void freeMemory(long var1); //释放内存,如果用了上面的方法,一定要用这个方法释放内存 +4.线程相关 + //唤醒线程 + public native void unpark(Object var1); + //阻塞线程 + public native void park(boolean var1, long var2); + +5.总结:先分析到这,有不足之处欢迎指出 + + + + + + +