java基础巩固

1.int Integer自动装箱与拆箱

装箱

Integer i = 10;

拆箱

int n = i;   //拆箱

装箱就是  自动将基本数据类型转换为包装类型;拆箱就是  自动将包装类型转换为基本数据类型。

装箱和拆箱是如何实现的

在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

2.代码输出结果

public class Main {

    public static void main(String[] args) {

         

        Integer i1 = 100;

        Integer i2 = 100;

        Integer i3 = 200;

        Integer i4 = 200;

         

        System.out.println(i1==i2);

        System.out.println(i3==i4);

    }

}

 输出结果为:true false

在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

3.Java类中不同代码块的执行顺序

4.栈(stack)、堆(heap)和方法区

String str = new String("hello");
变量str放在栈上,用new创建出来的字符串对象放在堆上,而"hello"这个字面量是放在方法区的。

5.锁

偏向锁/轻量级锁/重量级锁

这三种锁是指锁的状态,并且是针对Synchronized。在Java 5通过引入锁升级的机制来实现高效Synchronized。
偏向锁:是指一段同步代码一直被一个线程所访问,那么该线程会自动获取锁。降低获取锁的代价。
轻量级锁:是指当锁是偏向锁的时候,被另一个线程所访问,偏向锁就会升级为轻量级锁,其他线程会通过自旋的形式尝试获取锁,不会阻塞,提高性能。
重量级锁:是指当锁为轻量级锁的时候,另一个线程虽然是自旋,但自旋不会一直持续下去,当自旋一定次数的时候,还没有获取到锁,就会进入阻塞,该锁膨胀为重量级锁。重量级锁会让其他申请的线程进入阻塞,性能降低。

6.乐观锁

一般来说有以下2种方式:

版本号机制:

一般是在数据表中加上版本号字段 version,表示数据被修改的次数。当数据被修改时,这个字段值会加1。最后比较提交的数据版本号是否大于当前的数据版本号。

“提交版本必须大于记录当前版本才能执行更新” 的乐观锁策略。

CAS 算法:

即 compare and swap(比较与交换),是一种有名的无锁算法。无锁编程,即不使用锁(没有线程被阻塞)的情况下实现多线程之间的变量同步,所以也叫非阻塞同步(Non-blocking Synchronization)。CAS 算法涉及到三个操作数:

需要读写的内存值 V
进行比较的值 A
拟写入的新值 B
当且仅当 V 的值等于 A 时,CAS 通过原子方式用新值 B 来更新 V 的值,否则不会执行任何操作(比较和替换是一个 native 原子操作)。一般情况下,这是一个自旋操作,即不断的重试。


7.lock和synchronized区别及应用场景

 synchronized底层:其实synchronized映射成字节码指令就是增加来两个指令:monitorenter和monitorexit。当一条线程遇到monitorenter指令的时候,它会去尝试获得锁,如果获得锁那么锁计数器+1,如果没有获得锁,那么阻塞。当它遇到monitorexit的时候,锁计数器-1,当计数器为0,那么就释放锁。

lock底层: 

Lock锁的底层实现是AQS

AQS:抽象的队列式同步器

2.AQS 的核心思想

如果被请求资源空闲,则将当前线程设置为有效的工作线程,并将资源设置为锁定状态,如果被请求资源被占用,那么就需要线程阻塞及被唤醒时锁分配的机制,这个机制 AQS 是用 CLH 队列锁(CLH 锁是一个自旋锁,提供先来先服务的公平性)实现的,即将暂时获取不到锁的线程加入到队列中。 


8.线程池

1.优势:

降低资源消耗

提高响应速度

提高线程的可管理性

2.线程池的真正实现类是 ThreadPoolExecutor

3.参数

核心线程数

程池所能容纳的最大线程数

线程闲置超时时长

线程闲置超时时长单位

任务队列

(可选):线程工厂

(可选):拒绝策略

4. Executors 框架已经为我们实现了 4 种拒绝策略:

AbortPolicy(默认):丢弃任务并抛出 RejectedExecutionException 异常。
CallerRunsPolicy:由调用线程处理该任务。
DiscardPolicy:丢弃任务,但是不抛出异常。
DiscardOldestPolicy:丢弃队列最早的未处理任务

5.Executors已经为我们封装好了 4 种常见的功能线程池:

  • 定长线程池(FixedThreadPool)
  • 定时线程池(ScheduledThreadPool )
  • 可缓存线程池(CachedThreadPool)
  • 单线程化线程池(SingleThreadExecutor)

9.volatile和synchronized的区别

  • volatile告诉jvm当前变量在寄存器中的值是不确定的,需要从主存中读取; synchronized则是锁定当前变量
  • volatile仅能使用在变量级别;synchronized则可以使用在变量、方法、和类级别的
  • volatile仅能实现可见性,不能保证原子性;而synchronized则可以保证可见性和原子性
  • volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
  • volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化

10.HashMap

1.数据结构

HashMap 数据结构为 数组+链表,其中:链表的节点存储的是一个 Entry 对象,每个Entry 对象存储四个属性(hash,key,value,next)

2.容器默认的数组大小为 16,加载因子 为0.75。容器的阈(yu)值为 initialCapacity * loadFactor,默认情况下阈值为 16 * 0.75 = 12;

3.在JDK1.7时HashMap采取的是数组+链表的形式,JDK1.8对HashMap引入了红黑树数据结构,极大的增强了HashMap的存取性能!

为什么会引入红黑树呢?因为HashMap存在一个问题,即使负载因子和Hash算法设计的再合理,也无法避免出现在链表上拉链过长的问题,如果极端情况下出现严重的Hash冲突,会严重影响HashMap的存取性能,于是HashMap在jdk1.8时,引入了红黑树,利用红黑树快速增删改查的特点来优化了HashMap的性能!

4.HashMap之put方法

第一步:判断键值对数组是否为空,是则执行resize()扩容。

第二步:根据键key计算hash值得到插入数组的索引i,如果tab[i]== null则直接插入,执行第六步;如果tab[i] != null,执行第三步。

第三步:判断tab[i]的第一个元素与插入元素key的hashcode&equals是否相等,相等则覆盖,否则执行第四步。

第四步:判断tab[i]是否是红黑树节点TreeNode,是则在红黑树中插入节点,否则执行第五步。

第五步:遍历tab[i]判断链表是否大于8,大于8则可能转成红黑树(数组需要大于64),满足则在红黑树中插入节点;否则在链表中插入;在遍历链表的过程中如果存在key的hashcode&equals相等则替换即可。

第六步:插入成功,判断hashmap的size是否超过threshold的值,超过则扩容。

5.为什么要改成“数组+链表+红黑树”?

为了提升在 hash 冲突严重时(链表过长)的查找性能,使用链表的查找性能是 O(n),而使用红黑树是 O(logn)。

6.什么时候用链表?什么时候用红黑树?

默认情况下是使用链表节点。当同一个索引位置的节点在新增后达到9个(阈值8):如果此时数组长度大于等于 64,则会触发链表节点转红黑树节点;而如果数组长度小于64,则不会触发链表转红黑树,而是会进行扩容,因为此时的数据量还比较小。

7.HashMap 有哪些重要属性?分别用于做什么的?

除了用来存储我们的节点 table 数组外,HashMap 还有以下几个重要属性:

1)size:HashMap 已经存储的节点个数;

2)threshold:扩容阈值,当 HashMap 的个数达到该值,触发扩容。

3)loadFactor:负载因子,扩容阈值 = 容量 * 负载因子。
 

8.HashMap 的默认初始容量是多少?HashMap 的容量有什么限制吗?

默认初始容量是16。HashMap 的容量必须是2的N次方,

9.你说 HashMap 的容量必须是 2 的 N 次方,这是为什么?

计算索引位置的公式为:(n - 1) & hash,当 n 为 2 的 N 次方时,n - 1 为低位全是 1 ,此时任何值跟 n - 1 进行 & 运算的结果为该值的低 N 位,达到了和取模同样的效果,实现了均匀分布。实际上,这个设计就是基于公式:x mod 2^n = x & (2^n - 1),因为 & 运算比 mod 具有更高的效率。

当 n 不为 2 的 N 次方时,hash 冲突的概率明显增大。

10.HashMap 的插入流程是怎么样的?

 11.扩容(resize)流程介绍下?

 12. JDK 1.8 主要进行了哪些优化?

1)底层数据结构从“数组+链表”改成“数组+链表+红黑树”,主要是优化了 hash 冲突较严重时,链表过长的查找性能:O(n) -> O(logn)。

2)计算 table 初始容量的方式发生了改变,老的方式是从1开始不断向左进行移位运算,直到找到大于等于入参容量的值;新的方式则是通过“5个移位 + 或等于运算”来计算。

3)优化了 hash 值的计算方式,新的只是简单的让高16位参与了运算。

4)扩容时插入方式从“头插法”改成“尾插法”,避免了并发下的死循环。

5)扩容时计算节点在新表的索引位置方式从“h & (length-1)”改成“hash & oldCap”

13.除了 HashMap,还用过哪些 Map,在使用时怎么选择?

11.volatile关键字 

1.volatile关键字是由JVM提供的最轻量级同步机制

volatile关键字的特性

1.被volatile修饰的变量保证对所有线程可见。

volatile修饰的变量并不保值原子性

AtomicInteger实现了原子性,作为对照组

2.禁止指令重排序优化。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值