每天10个java面试题-day2

1.CurrentHashMap原理,jdk7和jdk8区别


jdk7:
数据结构:ReenTrantLock+segment+HashEntry,一个Segment中包含一个HashEntry数组,每个HashEntry又是一个链表数组
元素查询:二次hash,第一次hash定位到segment,第二次hash定位到元素所在的链表头部

segment分段锁,segment继承了ReenTrantLock,锁定操作的segment,其他的segment不受影响,并发度为segment个数,可以通过构造函数指定,数组扩容不会影响到其他的segment
get方法无需加锁,volatile保证

jdk8
数据结构:synchronized+CAS+Node+红黑树,Node的value和next都用volatile修饰保证可见性
查找、替换、赋值操作都使用CAS
:锁链表的head节点,不影响其他元素的读写,锁力度更细,效率更高,扩容时阻塞所有的读写操作、并发扩容
读操作无锁:
node的value和next使用volatile修饰,读写线程对该变量互相可见
数组用volatile修饰,保证扩容时被读线程感知

2.如何实现一个IOC容器


精简版

  1. 配置文件配置包扫描路径
  2. 递归包扫描获取.class文件
  3. 反射,确定要交给IOC管理的类
  4. 对需要注入的类进行依赖注入

详细版

  • 配置文件中指定需要扫描的包路径
  • 定义一些注解,分别表示访问控制层、业务服务层、数据持久层、依赖注入层、获取配置文件注解
  • 从配置文件中获取需要扫描的包路径,获取到当前文件下的文件信息及文件夹信息,我们将当前路径下所有以.class结尾的文件添加到一个set集合中进行存储
  • 遍历这个set集合,获取在类上有指定注解的类,并将其交给IOC容器,定义一个安全的Map用来存储这些对象
  • 遍历这个IOC容器,获取到每一个类的实例,判断里面是否有依赖其他的类的实例,然后进行递归注入

3.什么是字节码?采用字节码的好处是什么?


java中的编译器和解释器:

  • java中引入了虚拟机的概念,即在机器编译程序之间加入了一层抽象的虚拟机器。这台虚拟的机器在任何平台上都提供给编译程序一个共同的接口。
  • 编译程序只需要面向虚拟机,生成虚拟机能够识别的代码,然后用解释器来将虚拟机代码转化为特定系统的机器码执行。在java中,这种虚拟机理解的代码叫做字节码(即扩展名为.class的文件),它不面向任何的处理器,只面向虚拟机。
  • 每一种平台的解释器是不同的,但是实现的虚拟机是相同的。java源程序经过编译器编译后形成字节码,字节码由虚拟机解释执行,虚拟机将每一条要执行的字节码送个解释器,解释器将其翻译成特定机器上的机器码,然后在特定的机器上运行。这也就解释了java的编译与解释并存的特点。
    java源代码---->编译器---->jvm可执行的字节码(即虚拟指令)---->jvm---->jvm中的解释器 ---->机器可执行的二进制机器码---->机器运行
    采用字节码的好处:
    java语言通过字节码的方式,在一定程度上解决了传统解释型语言执行效率低的问题,同时又保留了解释型语言可移植的特点。所以java程序运行时比较高效,而且,由于字节码并不专对一种特定的机器,因此,java程序无需重新编译便可在多种不同的计算机上运行。

3.java类加载器有哪些?


JDK自带有三个类加载器:
bootstrapClassLoaderExtClassLoader(扩展类加载器)、AppClassLoader(系统类加载器)

  • bootstrapClassLoader:是ExtClassLoader父类加载器,默认负责加载%JAVA_HOME%lib下的jar包和class文件
  • ExtClassLoader:是AppClassLoader的父类加载器,负责加载%JAVA_HOME%lib/ext文件夹下的jar包和class类
  • AppClassLoader:是自定义加载器的父类,负责加载classpath下的类文件。系统类加载器,线程上下文加载器继承ClassLoader实现自定义类加载器

4.双亲委派模型


1

双亲委派的好处:

  • 主要是为了安全性,避免用户自己编写的类动态替换java的一些核心类, 比如String
  • 同时也避免了一些类的重复加载,因为jvm中区分不同类,不仅仅是根据类名,相同的class文件被不同的classLoader加载就是不同的两个类

5.java中的异常体系


  • java中所有的异常都来自顶级父类Throwable
  • Throwable下有两个子类:Exception和Error
  • Error是程序无法处理的错误,一旦出现这个错误,则程序将被迫停止运行
  • Exception不会导致停止,又分为两个部分,RuntimeException运行时异常和CheckedException检查异常。
  • RuntimeException常常发生在程序运行过程中,会导致程序当前线程执行失败。checkedException常常发生在程序编译过程中,会导致程序编译不通过。

6.GC如何判断对象可以被回收


  • 引用计数法:每个对象都有一个引用计数属性,新增一个引用时计数加1,引用释放时,计数减1,计数为0时可以回收

  • 可达性分析法:从GC root开始向下搜索,搜索所走过的路径称为引用链。当一个对象到GC Roots没有任何引用链相连时,则证明此对象是不可用的,那么虚拟机就判断是可回收对象
    引用计数法,可能会出现A引用了B,B又引用了A,这时候就会出现就算他们都不再被使用了,但因为相互引用,计数器=1,永远无法被回收
    GC Roots对象有:

    • 虚拟机栈(栈帧中的本地变量表)中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中常量引用的对象
    • 本地方法栈中JNI(即一般说的native方法)引用的对象

    7.线程的生命周期,线程有哪些状态


    1.线程通常有五种状态:创建、就绪、运行、阻塞、死亡
    2.阻塞的情况又分为三种:

    • 1)等待阻塞:运行的线程执行wait方法,该线程会释放占用的所有资源,jvm会把线程放入等到池中。进入这个状态后,是不能自动唤醒的,必须依靠其他线程notify或notifyAll方法才能唤醒,wait是Object类的方法
    • 2)同步阻塞:运行的线程在获取对象的同步锁时,若该同步锁被别的线程占用,则jvm会把该线程放入锁池中
    • 3)其他阻塞:运行的线程执行sleep或join方法,或者发出了I/O请求时,jvm会把该线程置为阻塞状态。当sleep状态超时、join等待线程终止或者超时、或者I/O处理完毕时,线程重新转入就绪状态。sleep是Thread类的方法

  1. 新建状态(New):新创建了一个线程对象
  2. 就绪状态(Runnable):线程对象创建后,其他线程调用了该对象的start方法。该状态的线程位于可运行线程池中,变得可运行,等待获取CPU的使用权
  3. 运行状态(Running):就绪状态的线程获取了CPU,执行程序代码
  4. 阻塞状态(Blocked):阻塞状态是线程因为某种原因放弃CPU使用权,暂时停止运行。直到线程进入就绪状态,才有机会转到运行状态。

8.sleep()、wait()、join()、yield()的区别


1.锁池:
所有需要竞争同步锁的线程都会放在锁池中,比如当前对象的锁已经被其中一个线程得到,则其他线程需要在这个锁池中进行等待,当前面的线程释放同步锁后锁池中的线程 去竞争同步锁,当某个线程得到后会进行就绪队列进行等待CPU资源分配
2.等待池:
当我们调用wait()方法后,线程会放到等待池当中,等待池的线程是不会去竞争同步锁。只有调用notify()或notifyAll()后线程池的线程才会开始去竞争锁,notify是随机从等待池选出一个线程放到锁池,而notifyAll()是将等待池的所有线程放到锁池当中

  • 1)sleep是Thread类的静态本地方法,wait则是Object类的本地方法
  • 2)sleep方法不会释放lock,但是wait会释放,而且会加入到等待队列中
    sleep就是把cpu的执行资格和执行权释放出去,不再运行此线程,当定时时间结束再取回cpu资源,参与cpu的调度,获取到cpu资源后就可以继续运行了。而如果sleep时该线程有锁,那么sleep不会释放这个锁,而是把锁带着进入了冻结状态,也就是说其他需要这个锁的线程根本不可能获取到这个锁。也就是无法执行程序。如果在睡眠期间其他线程调用了这个线程的interrupt方法,那么这个线程也会抛出interruptException异常返回,这点和wait是一样的
  • 3).sleep不依赖于同步器synchronized,但是wait需要依赖synchronized关键字。
  • 4).sleep不需要被唤醒(休眠之后推出阻塞),但是wait需要(不指定时间,需要被别人中断)
  • 5).sleep一般用于当前线程休眠,或者轮询暂停操作wait则多用于多线程之间的通信
  • 6).sleep会让出cpu执行时间且强制上下文切换,而wait则不一定,wait后可能还是有机会重新竞争到锁继续执行的

yield():执行后线程直接进入就绪状态,马上释放了cpu的执行权,但是依然保留了cpu的执行资格,所以有可能cpu下次进行线程调度还会让这个线程获取到执行权继续执行
join():执行后线程进入阻塞状态,例如在线程B中调用线程A的join()方法,那么线程B会进入到阻塞队列,直到线程A结束或中断线程

9.说说你对线程安全的理解


1

10.Thread和Runnable的区别

2
2

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值