Java面试题【JAVA教程】

!
也想出现在这里? 联系我们
信息

Java面试题,第1张

加载类的过程?

类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载7个阶段。

  1. 加载:通过一个类的全限定名来获取定义此类的二进制字节流,将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构,在内存中生成一个代表这个类的Class对象,作为方法区这个类的各种数据的访问入口
  2. 验证:验证是连接阶段的第一步,这一阶段的目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟自身的安全。
  3. 准备:准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(static),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在Java堆中。
  4. 解析:解析阶段是虚拟机将常量池内的符号(Class文件内的符号)引用替换为直接引用(指针)的过程。
  5. 初始化:初始化阶段是类加载过程的最后一步,开始执行类中定义的Java程序代码(字节码)。

HashMap底层为什么使用红黑树?

好处就是避免在最极端的情况下链表变得很长很长,在查询的时候,效率会非常慢。

红黑树查询:其访问性能近似于折半查找,时间复杂度 O(logn);

  • 链表查询:这种情况下,需要遍历全部元素才行,时间复杂度 O(n);
  • 简单的说,红黑树是一种近似平衡的二叉查找树,其主要的优点就是“平衡“,即左右子树高度几乎一致,以此来防止树退化为链表,通过这种方式来保障查找的时间复杂度为 log(n)。

红黑树的特点:

  1. 每个节点要么是红色,要么是黑色,但根节点永远是黑色的;
  2. 每个红色节点的两个子节点一定都是黑色;
  3. 红色节点不能连续(也即是,红色节点的孩子和父亲都不能是红色);
  4. 从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点;
  5. 所有的叶节点都是是黑色的(注意这里说叶子节点其实是上图中的 NIL 节点);

如果你的应用中,搜索的次数远远大于插入和删除,那么选择AVL树,

如果搜索,插入删除次数几乎差不多,应选择红黑树

值传递和引用传递

值传递是对基本型变量而言的,传递的是该变量的一个副本,改变副本不影响原变量.
引用传递一般是对于对象型变量而言的,传递的是该对象地址的一个副本, 并不是原对象本身 。

AQS独占锁和共享锁

ReentrantLock会保证执行do something在同一时间有且只有一个线程获取到锁,其余线程全部挂起,直到该拥有锁的线程释放锁,被挂起的线程被唤醒重新开始竞争锁。

共享功能的主要实现为CountDownLatch,CountDownLatch是一种灵活的闭锁实现,它可以使一个或多个线程等待一组事件发生。闭锁状态包括一个计数器,该计数器被初始化为一个正数,表示需要等待的事件数量。countDown递减计数器,表示有一个事件已经发生了,而await方法等待计数器达到零,这表示所有需要等待的时间都已经发生。如果计数器值非零,那么await会一直阻塞直到计数器为零,或者等待线程中断,或者等待超时。

ReentrantLock中lock和tryLock的区别

1: lock拿不到锁会一直等待。tryLock是去尝试,拿不到就返回false,拿到返回true。

2: tryLock是可以被打断的,被中断 的,lock是不可以。

Synchronized和ReentrantLock的区别

Synchronized锁升级的过程

无锁 -> 偏向锁 -> 轻量级锁 -> 重量级锁

大多数时候是不存在锁竞争的,常常是一个线程多次获得同一个锁,因此如果每次都要竞争锁会增大很多没有必要付出的代价,为了降低获取锁的代价,才引入的偏向锁。

一开始是无锁状态。此时第一个线程进来了,在对象头Mark Word中看到此时是无锁状态,就把此时的锁升级为偏向锁,并将自己的线程id用CAS的方式赋值到Mark Word中。然后就进入到了该线程的同步块中。

如果此时有第二个线程进来,它会去查看当前偏向锁的线程id是否是自己,结果发现不是,但是此时还是会去CAS尝试修改线程id指向自己,去赌一下第一个线程此时是否已经释放了。如果释放了,它会将锁状态改为无锁状态,将线程id置空。然后第二个线程拿到这个资源,将线程id赋值给自己,锁升级为偏向锁。如果第一个线程没有释放,JVM会在第一线程到达安全点的时候撤销当前的偏向锁。拷贝Mark Word到锁记录中。然后两个线程用CAS的方式去修改Mark Word中的指针指向自己,假如说第一个线程修改成功了,然后将锁升级为轻量级锁,去执行同步语句块中的内容。修改失败的第二个线程会进入自旋状态,自旋结束后会继续去尝试CAS修改指针指向自己。如果自旋失败超过一定次数的时候,会请求JVM将此时的锁状态升级为重量级锁,这是依赖于底层 *** 作系统的调度库实现的。

悲观锁和乐观锁

每次读取数据的时候,都会担心数据被修改,所以每次查询数据的时候都会加锁,确保自己在读取数据的时候不会被别人修改。使用完成后对数据经行解锁,由于数据经行加锁,期间对该数据进行读写的其他线程都会进行等待。

每次获取数据的时候,都不会担心数据被修改,所以每次获取数据的时候都不会进行加锁。但是在更新数据的时候需要判断该数据是否被别人修改过。如果数据被其他线程修改,则不进行数据更新,如果数据没有被其他线程修改,则进行数据更新。由于数据没有进行加锁,期间该数据可以被其他线程进行读写 *** 作。

悲观锁:比较适合写入 *** 作比较频繁的场景,如果出现大量的读取 *** 作,每次读取的时候都会进行加锁,这样会增加大量的锁的开销,降低了系统的吞吐量。

乐观锁:比较适合读取 *** 作比较频繁的场景,如果出现大量的写入 *** 作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询 *** 作,降低了系统的吞吐量。

CountDownLatch和Semaphore的区别和底层原理

MySQL的执行流程

RPC和Restful的区别

1.restfull和rpc都是client/server模式的,都是在 Server端 把一个个函数封装成接口暴露出去

2.restful使用http协议实现,而rpc则不一定使用http,一般比较常用的是tcp, RPC 可以获得更好的性能(省去了 HTTP 报头等一系列东西),TCP更加高效,而HTTP在实际应用中更加的灵活。

3.从使用上来说:Http接口只关注服务提供方(服务端),对于客户端怎么调用,调用方式怎样并不关心;而RPC服务则需要客户端接口与服务端保持一致,服务端提供一个方法,客户端通过接口直接发起调用。

© 版权声明
THE END
喜欢就支持一下吧
点赞159 分享
评论 抢沙发

请登录后发表评论

    请登录后查看评论内容