面试准备知识总结--java

arrayList:

        arratlist 内部使用了一个obj类型的数组,初识容量为10;

        arraylist线程不安全的原因是,arraylist添加元素的过程并不具备原子性

       arraylist可以无限的添加元素,因为arraylist 每次添加元素之前都会跟list的容量相比较,若没有指定初始容量,则扩容时,       按照原容量10,来进行扩容

       如果容量小了就会发生扩容 扩容大小为原数组的大小加上原数组长度的一半。(若原来的数组大小,小于添加一个元素之后的size,就发生扩容)

        如果扩容之后还不够,那么容量就是这次添加元素之后容器的size长度

       扩容之后,旧数组指向新的数组,使用了arrays.copyof()方法来复制了原数组中的内容

       arraylist数组删除某个索引位置处的数据耗时是因为,需要使用system.arraycopy方法来移动整个数组

       Fail-Fast机制是根据内部的modcount变量来验证的。用来保护数据的安全行

String:

       String不可变的原因是,内部使用了char[]数组来构建String字符串,每次new 一个新的字符串的时候,都会将内部的char[]数组来指向新的字符串。

StringBuilder:

        StringBuilder内部也使用了数组来表示String字符串,不同的是,StringBuilder支持动态扩容,初始容量为16,若当前字符数组的长度 < append()之后的长度,那么就扩容,扩容大小为原容量的大小 * 2 + 2。使用system.ArrayCopy()来完成数组的复制工作。

StringBuffer:

        与StringBuilder相同,不过该版本是线程安全的。全部使用synchronized来保证线程安全。

integer:

        内部有一个静态内部类IntegerCache,静态内部类内部使用integer[]数组,用来缓存 -127 - 128之间的数,使用静态代码块,来在类加载的时候,完成初始化工作

        Integer s = 3; 会发生自动装箱的事情,也就是说s会被包装成integer类型,并且 3是数组中已经缓存好的数值

       直接通过new关键子创建出来的integer对象是不同的对象,因为在堆中分配了不同的内存地址

      integer s = 3; s == 3?  包装类型和基本类型之间的比较,比较的数字部分。其他的包装类与其相同,内部都使用了缓存

hashmap (jdk1.7):

        hashmap实现map<k,v>接口

        默认容量16,默认装载因子是0.75,最大容量是2^30

 

        内部有一个静态内部类entry,表示一个mapping映射

        hashmap使用entry数组来接收k,v键值对,数组的默认大小是16

        (hashmap初始化问题)hashmap初始化的时候,若没有给容量,那么默认threadhold为16,put时,若hashmap为空,那么会初始化容量,若threadhold大于1,那么就是(threshold - 1) << 1,否则就是1

        (扩容问题)之后确定threadhold的值 = hashmap的容量 * 加载因子(loadFactory 0.75f),若hashmap的size > threhold
          那么hashmap会发生扩容,容量为原来容量的两倍,并且会遍历原来的hashmap,从新hash(key),将旧的值复制到新的hashmap中新的位置(每个entry下的链表还是在同一个链表下),是一个比较费时的操作,应该尽量避免扩容。

        (加载因子的问题)不能太大,也不能太小,太大的话hashmap中的元素太多,更容易发生hash冲突,太小的话元素稀疏,浪费空间。

        (确定entry节点位置的问题)使用 hashmap.lemgth - 1 & hash(key) 使得元素分布的更加均匀

        (key为null的情况)若put的k,为null 那么在table[0]的位置上查找k为null的entry节点,若找到,那么覆盖原来的value,返回旧值
        若没有找到,那么在table表中找到对应的位置,放入,若该节点的K为null那么k的hash值为0,且放在table【0】的位置上

        (key不是null的情况)若k不是null,算出key的hash值,根据hash值,找到table表中对应的位置,若该表中存在元素,且key的hash值,key值;
        都等于现在put的元素,那么用现在的value值覆盖原来的旧value值,且返回旧value值

        (hash冲突)若key的hash值相同,但是key不相同的情况下,那么就发生了hash冲突,hashmap会将现在最新的entry节点放入对应的table中,将旧的ertry节点,作为现在节点的next节点。

        (hashmap hashtable的区别)hashtable是线程安全的,使用了synchronized关键字来保证线程安全,而hashmap是线程不安全的。
        hashtable不允许key为null ,而hashmap可以
        hashmap中定位entry元素的位置使用hash(key) & hashmap.length - 1.而 hashtable是取模运算。

        (使得hashmap线程安全的方法) 使用Collections.synchronizedMap(map),该方法将map修改为final域对象,并且在对hashmap中元素的读写时,增加了synchronized关键字来保证线程安全;

        accseeOrder属性,默认为false,表示按照插入顺序来访问entry对象,true时表示按照访问顺序来访问对象

        (get方法解读)若key为null,若hashmap的size为0,那么返回返回null,否则在在table【0】的链表中匹配key为null的entry
        若匹配到,那么返回value,否则返回null。
        若key不是null,那么就根据hash(key) & hashmap.length算出该entry在table中位置,若hash(key), key都相同那么返回对应的entry,否则返回null
        之后返回entry的value值。

       LinkedHashMap与hashmap相同,区别就是LinkedHashMap使用了双向链表,更加利于增加删除节点

lock : 

        (排他锁,公平锁):获取锁的过程

                首先lock的实现依赖于AbstractQueuesynchronizer(队列同步器)

                同步器中有一个静态内部类Node表示当前线程获取锁的状态,以及当前线程的前驱,后继,以及下一个等待的节点

        获取独占锁的过程(公平):

            获取锁的时候会先判断此时锁是否被占有,在lock中使用state变量来表示,0表示没有占有,1表示占有

            若此时没有被占有,判断aqs队列中是否有节点,若没有节点,尝试使用CAS算法来更新state状态,若更新成功,表示获取锁,返回true,否则表示获取锁失败,返回false;

            若当前线程是重入获取锁,将state状态设置为+1,返回true,获取锁成功。

            若获取锁失败,将该线程包装为节点放入aqs中。

            放入队列中尝试获取锁,若当前节点的前驱节点是头节点,那么可以尝试获取锁,若CAS成功,表示获取锁,将该节点设置为头节点。若获取失败,则将当前线程挂起,返回中断状态,以死循环的方式不断的来尝试获取锁。

        (排他锁,非公平):

            过程如上,但是此时线程获取锁,不需要判断aqs中是否有节点,直接使用CAS来更新状态,若成功,那么获取锁,否则加入到aqs对列中,以死循环的方法来不断的尝试。

        释放的过程:

            因为锁是重入锁,所以直到state为0时才真正释放锁。释放锁之后,将该线程的等待状态置为0,唤醒下一个阻塞的线程

          写锁的获取与释放 : 

                        过程与上述相同。

        semaphore(java并发工具,用以支持限制线程的并发数):

            使用方法:semaphore.acquire()获取锁    semaphore.release(); 释放锁

            实现原理:主要依靠CAS算法,和一个数值状态来完成,数值状态表示可以同时运行的线程数,每当有线程尝试更改数值状态时:若数值状态小于0表示此时不再支持并发,将该线程加入对列以死循环的方式来不断的获取锁。否则表示可以同时并发

       CyclicBarrier(java并发工具,用来支持,在某一时刻,所有准备就绪的线程同时运行)

        使用方法:cyclicBarrier.await();

        实现原理:内部使用了lock排他锁,使用排他锁来互斥的修改内部变量count,若count != 0 那么当前线程将使用condition.await()方法来被阻塞,若count == 0,那么将调用singal()方法来唤醒阻塞再次的线程

       CountDownLatch(java并发工具,用以支持,线程等待某个最终信号出现之后在运行):

        使用方法:countDownLatch.await(); 线程等待某个信号  countDownLatch.countDown(); 修改信号,来达到最终信号

         实现原理:与共享锁实现原理相同

            

猜你喜欢

转载自blog.csdn.net/qq_32182461/article/details/80697303