面试准备知识总结--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为12,put时,若hashmap为空,那么会初始化容量,若threadhold大于1,那么就是取该数值(threshold - 1) << 1 的二进制的最高位,剩余位置补0表示的十进制数值,也就是2的n次方的大小,否则就是初始化容量为1

        (扩容问题put时,若发现此时的容量大于threahold,那么会进行扩容,容量为原来的2倍,也就是2的n次方,之后重新计算每个key的hash值,将旧链表迁移到新的enter数组中,链表的顺序与原来链表的顺序相反,就是原来在表头的元素,迁移之后到了链表尾部。
        (加载因子的问题)不能太大,也不能太小,太大的话hashmap中的元素太多,更容易发生hash冲突,太小的话元素稀疏,浪费空间。

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

数组是2的n次方的数组,插入新节点造成冲突的概率更低,这样在查找元素的时候,尽量避免的遍历链表,若长度不是2的n次方,会造成许多的二进制末尾为1的entry槽位不能得到使用造成空间的浪费。

        (key为null的情况)若put的k,为null 那么在table[0]的位置上查找k为null的entry节点,若找到,那么覆盖原来的value,返回旧值
        若没有找到,那么在table表中找到对应的位置,放入,若该节点的K为null那么k的hash值为0,且放在table【0】的位置上,put的过程中涉及到扩容问题,也就是resiez函数,扩容的大小为原来的2倍,还是2的n次方

        (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值。

       (hashmap成环原因a,b线程同时触发扩容过程,但是a在执行到 Entry<K,V> next = e.next;这句话时时间片停止,b开始执行,并且完成一次该过程,b线程完成之后,a线程所谓的e已经变成了newtable【i】所指向的内存,所以e.next = newtable[i]相当于自己引用自己,所以会造成环,在执行get过程的时候形成死循环。

hashmap 1.8与1.7的区别:

    1.8会在链表长度大于8时转变成红黑树,树的高度很低,便于查找效率。

    1.8中将entry[]数组转换为node数组,本质上是一样的。

    1.8在发生hash冲突时若此处没有链表,那么会将新的k,v放在链表的后面,而不是1.7那样放在前面

    1.8的扩容依旧是容量是之前的两倍,也就是2的n次方,迁移数据的时候若结点是treenode节点就按照红黑树的方式迁移,若节点是链表节点,那么原链表将会被拆成两个两个链表,一个是槽位相对于原来来说没有发生变更的,一个是发生变更的,两条链表之间的距离为老链表的长度。

    1.8在并发扩容是依旧会成环。成环的原因依旧是自己引用自己。

concurrentHashmap (jdk 1.7)

 (初始化问题) 初始化时涉及到三个参数 

initialCapacity(map的初始容量,表示segent下面hashmap的数量),loadFactor(加载因子), concurrencyLevel(并行级别,也就是sengent数组的大小,默认是16,表示可以支持同时16个线程并发修改数据)

根据并行级别确定segent数组的大小,segent数组会从1开始以2的n次方开始增加,直至超过并行级别的大小,内部使用ssize表示segent数组的大小

 

while (ssize < concurrencyLevel) {
    ++sshift;
    ssize <<= 1;
}

确定好ssize之后,根据 initialCapacity / ssize确定每个sengent元素分配几个hashmap。在默认情况下 initialCapacity = 16

ssize = 16 因此每个sengent会分配一个hashmap,之后会使用ssize初始化一个sengent数组,但是数组中只有sengent[0]会被初始化。

int c = initialCapacity / ssize;
if (c * ssize < initialCapacity)
    ++c;
Segment<K,V> s0 =
    new Segment<K,V>(loadFactor, (int)(cap * loadFactor),
                     (HashEntry<K,V>[])new HashEntry[cap]);
Segment<K,V>[] ss = (Segment<K,V>[])new Segment[ssize];

至此就完成了容器的初始化。

(put过程)

 

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(); 修改信号,来达到最终信号

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

 

 

            

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值