1、一个静态方法内调用一个非静态成员为什么是非法的?
静态方法数据类本身,在类加载时分配到内存。
非静态成员属于对象,只有在类被实例化时才能被加载到内存,因此当静态方内调用非静态成员时,非静态成员可能不存在,加载一个不存在的东西肯定会报错
2、为什么两个对象有相同的hashCode,两个对象不一定相等?
==:如果是数值,则进行值比较,如果是对象则判断两个对象jvm中的地址
equals底层实现
从中可以看出,当比较值为字符串时,则直接进行字符串比较,原因为:String 是使用比较频繁的对象类,所以JVM为了优化性能和减少开销,在内存中单独开出一块区域,即字符串常量池,需要使用时,先看字符串常量池中是否存在,如果不存在,则初始化,把字符串放入常量池,如果存在则直接使用。
3、线程有哪些基本状态:
开始:线程开始创建还未启动
运行:此时运行为两部分,running和ready, running标识线程已经启动,ready标识线程等待CPU分配时间
等待:分为无限期等待和有限期等待,无限期等待是无timeout时间的wait,没有timeout的Thread.join(),需要外部显示唤醒才能等待结束,有限期等待为Thread.sleep(),带timeout时间的Object.wai()和带timeout的Thread.join(),当到达一定时间后会自动唤醒。
阻塞:如果线程被阻塞,则阻塞和等待区别为:阻塞需要获取一个排它锁,这个将在其他线程释放这个锁开始,等待则为等待一段时间或唤醒动作的发生。
结束:已经终止的线程状态,当前线程已结束。
4、什么是自动拆箱?什么是自动封箱?
自动拆封箱是基本数据类型整数类型(int,byte,short,long)、字符类型(char)、布尔类型(boolean)、浮点类型(double,float)包装类转化
5、java常见IO阻塞模型
为了保护数据安全,将内存分为两部分:
1、用户空间
2、内存空间
获取数据的过程也可以分为两步:
1、用户空间从内存空间中申请,等在内存空间获取数据
2、当内存空间获取到数据后,从内存空间拷贝到用户空间
几种io模型:
1、阻塞io
常规io模型,用户线程获取数据时,阻塞等待内存空间获取数据,直到存空间数据获取完毕,内存空间将数据拷贝到用户空间,阻塞结束,用户线程进行数据处理。
2、非阻塞io
设置socket为NIO(NONBLOCK),用户线程从内存空间获取数据发起io请求后立即返回,然后不断的发起io请求直到内存空间获取到数据,将数据从内存空间拷贝到用户空间,用户线程读取完毕。
3、多路复用io
用户将需要监控的socket注册到select中,然后阻塞等待select返回,当数据到达内存空间后,socket被激活,select返回,用户线程发起IO请求,读取完成后进行后续操作。
优点:用户可以注册多个socket到select,然后不断读取select状态,从而实现一个线程管理多个IO请求的目的,而在同步阻塞模型中,必须通过多线程才能实现。
虽然上述模式允许单个线程内处理多个 IO,但是每个IO还是阻塞的(select函数阻塞),平均时长甚至比同步阻塞IO还要长,但是用户可以只注册自己感兴趣的IO请求,当数据准备完毕后,socket被激活,用户才进行数据读取,从而提高CPU利用率。
多路复用IO基于Reactor设计模式实现了这一机制
EventHandler抽象类表示IO文件事件处理器,他包含文件句柄handler和对文件处理事件操作handler_event()。继承EventHandler可以对文件事件处理进行定制。Reactor类则对EventHandler进行管理(注册、删除),使用handler_events()进行事件循环,通过不断轮询多路分离器的多路分离函数select,当某一个文件句柄被激活,select就返回,handler_events就会调用与句柄相关的handler_event进行后续文件处理。
总之:通过Reactor方式,可以将轮询IO状态的工作统一交给handler_events()进行处理,当用户线程注册时间处理器后可以进行其他操作,Reactor线程则不断调用select函数查询socket状态,当有socket被激活后,则通知用户线程,执行handler_event时间处理。
6、CPU上下文切换
什么是cpu上下文
任务执行过程中,CPU需要知道从哪里加载,从哪里运行,所以需要系统分配CPU寄存器(CPU内置一个空间极小,运行速度极快的内存)和程序计数器(用来存储当前指令位置或者下一次指令位置),他们是CPU运行任务前,所依赖的环境,也称为CPU上下文。
CPU上下文切换的几种方式
--进程上下文切换
Linux根据特权等级,将进行运行空间分为用户空间和内核空间,进程在两个空间运行分别叫做用户态和内存态,用户态和内存态跳转是通过系统调用完成的。
系统调用:CPU寄存器首先保存用户态的指令,为了在内存态运行任务,需要刷新CPU寄存器为内存态指令,然后跳转到内存态运行任务,任务运行完毕后,CPU寄存器恢复用户态指令,然后跳回到用户空间,进程继续运行。所以系统调用时发生了两次CPU上下文切换,需要注意的是系统调用不会涉及到虚拟内存等用户空间的资源,也不会切换进程,所以系统调用和进程切换是有区别的:
进程切换是指从一个进程切换到另一个进程
系统调用是在同一个进程中执行的
--系统调用和进程上下文切换的区别:
进程是在内核中管理和切换的,进程的切换只能在内核中完成,进程的切换不仅包含用户空间的虚拟内存、栈、全局变量等资源,还包含内核堆栈,寄存器等内核资源。
进程上下文切换比系统调用多了一步,在保存内存态状态和CPU寄存器之前,首先需要保存虚拟内存,栈等,当内核态进程切换完成之后,还需要刷新虚拟内存和用户栈等
2、线程上下文切换
线程是调度的基本单位,进程是资源拥有的基本单位,所谓内核中的任务调度,实际调度对象是线程,进程只是提供虚拟内存、栈、全局变量等资源。所以线程上下文切换分为两种情况:
--不同进程之间的线程切换,等同于进程之间的切换
--相同线程之间的切换,线程共享的虚拟内存、全局变量等资源无需切换,只是切换线程私有的栈和寄存器等。
3、中断上下文切换
为了快速响应硬件,中断程序需要中断当前正常运行的进程,来运行中断程序,在中断进程前,首先保存被中断进程状态,当中断程序执行完毕后,被打断进程恢复继续执行。