• JDK1.8源码阅读系列之三:Vector


     本篇随笔主要描述的是我阅读 Vector 源码期间的对于 Vector 的一些实现上的个人理解,用于个人备忘,有不对的地方,请指出~

      先来看一下 Vector 的继承图:    可以看出,Vector 的直接父类是 AbstractList(已在JDK1.8源码阅读系列之一介绍过), 直接子类是 Stack(下一篇文章介绍)。

      在 Vector 类源码中,我认为有以下几个地方值得注意:

      1、Vector 类几乎对每一个独立操作(除 Iterator 相关方法)都实现了同步,即在每个方法上几乎都增加了 synchronized 关键字来实现线程间的同步。

      

      2、Vector 扩容方法 grow()

    复制代码
     1  private void grow(int minCapacity) {
     2         // overflow-conscious code
     3         int oldCapacity = elementData.length;
     4         int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
     5                                          capacityIncrement : oldCapacity);
     6         if (newCapacity - minCapacity < 0)
     7             newCapacity = minCapacity;
     8         if (newCapacity - MAX_ARRAY_SIZE > 0)
     9             newCapacity = hugeCapacity(minCapacity);
    10         elementData = Arrays.copyOf(elementData, newCapacity);
    11 }
    复制代码

      可见 Vector 的扩容量是由 oldCapacity 与 capacityIncrement 共同决定的,capacityIncrement 是我们可控的一个参数,在构造方法中传入,即每次容量具体增加多少,如果我们在创建 Vector 对象是没有指定 capacityIncrement,则默认每次把容量增加为原来的二倍(不越界的情况下)。

      3、Vector 类中的 subList()方法我认为值得注意,源码如下:

    1 public synchronized List<E> subList(int fromIndex, int toIndex) {
    2         return Collections.synchronizedList(super.subList(fromIndex, toIndex),
    3                                             this);
    4 }

      可见 Vector 调用的是其父类 AbstractList 类中的 subList 方法,并利用 Collections.synchronizedList() 进行装饰,下面看下 Collections.synchronizedList() 方法:

    复制代码
    public class Collections {
         ......
        static <T> List<T> synchronizedList(List<T> list, Object mutex) {
            return (list instanceof RandomAccess ?
                    new SynchronizedRandomAccessList<>(list, mutex) :
                    new SynchronizedList<>(list, mutex));
        }
        static class SynchronizedList<E>
            extends SynchronizedCollection<E>
            implements List<E> {
            private static final long serialVersionUID = -7754090372962971524L;
    
            final List<E> list;
    
            SynchronizedList(List<E> list) {
                super(list);
                this.list = list;
            }
            SynchronizedList(List<E> list, Object mutex) {
                super(list, mutex);
                this.list = list;
            }
            public E get(int index) {
                synchronized (mutex) {return list.get(index);}
            }
            public E set(int index, E element) {
                synchronized (mutex) {return list.set(index, element);}
            }
            public void add(int index, E element) {
                synchronized (mutex) {list.add(index, element);}
            }
            ......
         }
         
         ......
    }        
    复制代码

      可见 Collections.synchronizedList() 方法中传入的两个参数 一个是用于底层数组操作的 list,一个是信号量,用于线程间同步。在 vector 类的 subList 方法中,我们传入的参数为 AbstractList 类 subList 方法产生的基于 vector 的 list(对此 list 上进行的操作都会反应到 vector 当中去),还有把 vector 作为信号量参数传入,之所以要把 vector 对象作为信号量,是因为产生的 subList 底层是基于 vector 的,对此 subList 上进行的同步操作应与 vector 上进行的同步操作条件相同,防止同步出现问题。

      4、为什么java Vector类被认为是一个遗留的,过时的或废弃的类?在并发操作时,使用它是无效的吗?

      Vector中对每一个独立操作都实现了同步,这通常不是我们想要的做法。对单一操作实现同步通常不是线程安全的(举个例子,比如你想遍历一个Vector实例。你仍然需要申明一个锁来防止其他线程在同一时刻修改这个Vector实例。如果不添加锁的话通常会在遍历实例的这个线程中导致一个ConcurrentModificationException)同时这个操作也是十分慢的(在创建了一个锁就已经足够的前提下,为什么还需要重复的创建锁)当然,即使你不需要同步,Vector也是有锁的资源开销的。

      总的来说,在大多数情况下,这种同步方法是存在很大缺陷的。正如Mr Brain Henk指出,你可以通过用Collections.synchronizedList来装饰一个集合 ,事实上 ,Vector 将“可变数组”的集合实现与“同步每一个方法”结合起来的做法是另一个糟糕的设计。

      各个装饰方法能够更明确的指示其关注的功能实现。

      对于其子类 Stack ,也可使用双端队列Deque/ArrayDeque来实现栈操作,比如:

      

    复制代码
     1 public static void main(String[] args) {
     2         Deque<Integer> deque = new ArrayDeque<>();
     3         deque.push(1);
     4         deque.push(2);
     5         deque.push(3);
     6         System.out.println(deque.peek());//返回栈顶元素 不删除
     7         System.out.println(deque.size());
     8         while (!deque.isEmpty()) {
     9             System.out.println(deque.pop());
    10         }
    11 }
    复制代码

       如果你觉得本篇文章对你有用的话,请随手点击推荐,让更多的人看到,感谢!

  • 相关阅读:
    Springboot学习:核心配置文件
    Springboot学习:底层依赖与自动配置的原理
    Springboot学习:介绍与HelloWorld
    js根据时间戳倒计时
    windows phone 豆瓣api的封装
    Android开发初始
    PHP(一)
    程序员修炼之道(一)
    WebClient和HttpReuqest两种网络请求的方式
    黑客与画家(二)
  • 原文地址:https://www.cnblogs.com/chenliyang/p/6543774.html
Copyright © 2020-2023  润新知