互联网大厂Java面试:核心知识大考验
面试官:请简要介绍一下Java中的多线程。
王铁牛:多线程就是多个线程同时执行嘛。在Java里可以通过继承Thread类或者实现Runnable接口来创建线程。
面试官:那如何确保多线程之间的安全呢?
王铁牛:可以用synchronized关键字,它能保证同一时刻只有一个线程访问被修饰的代码块或方法。
面试官:讲得不错。那再说说线程池吧,在什么场景下会用到线程池?
王铁牛:当有大量并发任务时,使用线程池可以避免频繁创建和销毁线程,提高性能。
第一轮提问结束。
面试官:谈谈JVM的内存模型。
王铁牛:JVM内存模型包括堆、栈、方法区等。堆是存放对象实例的地方,栈是存放局部变量等,方法区存class文件结构等。
面试官:那类加载机制是怎样的?
王铁牛:类加载机制有加载、验证、准备、解析、初始化这几个阶段。
面试官:垃圾回收算法有哪些?
王铁牛:有标记清除算法、标记整理算法、复制算法等。
第二轮提问结束。
面试官:请说一下HashMap的底层实现。
王铁牛:HashMap底层是数组加链表再加红黑树。当链表长度超过一定阈值就会转成红黑树。
面试官:那HashMap在多线程环境下会有什么问题?
王铁牛:可能会出现数据丢失、死循环等问题。
面试官:如何解决HashMap在多线程环境下的问题?
王铁牛:可以用ConcurrentHashMap,它是线程安全的。
第三轮提问结束。
面试官:今天的面试就到这里,回去等通知吧。
答案:
多线程:
- 多线程是指程序中包含多个执行单元,这些执行单元可以并发执行。在Java中,创建线程有两种常见方式:继承Thread类和实现Runnable接口。继承Thread类时,子类重写run方法定义线程执行逻辑;实现Runnable接口时,实现类实现run方法。
- 确保多线程安全的方式之一是使用synchronized关键字。synchronized可以修饰代码块或方法,当一个线程访问被synchronized修饰的代码块或方法时,其他线程需要等待该线程执行完毕释放锁后才能访问。
线程池:
- 线程池适用于有大量并发任务的场景。当有大量任务需要处理时,如果频繁创建和销毁线程会消耗大量系统资源,降低性能。使用线程池可以预先创建一定数量的线程,任务提交时从线程池中获取线程执行,执行完后线程不会销毁而是放回线程池,等待下一个任务,从而提高系统的整体性能和响应速度。
JVM内存模型:
- 堆:是JVM中最大的一块内存区域,用于存放对象实例。
- 栈:主要存放局部变量、方法调用等信息。每个线程都有自己独立的栈空间。
- 方法区:用于存储已被虚拟机加载的类信息、常量、静态变量等数据。
类加载机制:
- 加载:将类的字节码文件加载到内存中。
- 验证:检查加载的字节码文件是否符合JVM规范。
- 准备:为类的静态变量分配内存并设置初始值。
- 解析:将符号引用转换为直接引用。
- 初始化:执行类的静态代码块和为静态变量赋值。
垃圾回收算法:
- 标记清除算法:先标记出所有需要回收的对象,然后统一回收。
- 标记整理算法:标记出存活对象后,将存活对象向一端移动,然后清理边界以外的内存。
- 复制算法:将内存分为两块,每次只使用其中一块,当这一块内存满了,将存活对象复制到另一块,然后清理原来的那一块。
HashMap底层实现:
- HashMap底层由数组、链表和红黑树组成。当向HashMap中插入元素时,首先根据key的hash值计算出在数组中的位置,如果该位置为空,则直接插入新节点;如果不为空,则会形成链表或红黑树(当链表长度超过一定阈值时会转换为红黑树),新节点插入到链表或红黑树的头部。
HashMap在多线程环境下的问题及解决方法:
- 问题:在多线程环境下,HashMap可能会出现数据丢失、死循环等问题。例如在扩容时,可能会导致链表形成环形结构,从而在获取元素时出现死循环。
- 解决方法:使用ConcurrentHashMap。ConcurrentHashMap通过分段锁机制保证线程安全,在进行写操作时,只对需要修改的段加锁,其他段可以继续读操作,大大提高了并发性能。