学习java基础知识的第五天

1、什么是线程池?

线程池是一种常见的线程管理方式,它是一组已经创建好的线程集合,可以重复利用这些线程来执行多个任务,从而避免了线程频繁创建和销毁的开销。线程池维护了一个任务队列,将待执行的任务提交到任务队列中,然后从池中取出一个空闲线程来执行任务,任务执行完成后,线程并不会被销毁,而是被放回到线程池中,等待下一个任务的到来。

线程池的主要作用是:

提高系统性能:避免了频繁创建和销毁线程的开销,提高了系统的响应速度。

提高线程的可管理性:线程池可以限制系统中的线程数量,防止线程过多导致系统资源的浪费和竞争,从而提高了线程的可管理性。

提高代码的复用性:线程池可以重复利用线程,避免了线程创建和销毁的重复操作,从而提高了代码的复用性。

线程池是多线程编程中非常重要的概念,被广泛应用于各种服务器端程序和高并发场景中。

2、线程池有哪几种状态?

1)RUNNING:线程池正常运行,可以接受新的任务,并且正在执行任务队列中的任务。

2)SHUTDOWN:线程池正在关闭中,不再接受新的任务,但是会继续执行任务队列中的任务,直到所有任务都执行完毕。

3)STOP:线程池正在快速关闭中,不再接受新的任务,并且会尝试中断正在执行中的任务,使得任务尽快终止。

4)TIDYING:线程池已经完成了所有任务的执行,但是仍有一些线程需要被关闭,进入这个状态时,线程池会尝试通过一系列操作,如中断空闲线程等,来将线程数量逐步减少至零。

5)TERMINATED:线程池已经完全终止,不再接受新的任务,也没有任何线程在运行。如果要重新使用线程池,需要重新创建一个新的线程池对象

3、Sychronized和ReentrantLock不同点

SychronizedReentrantLock
自动加锁和释放锁手动加锁和释放锁
jvm层面的锁api层面的锁
公平锁公平或者非公平即可
可响应中断,可轮回不可以响应中断

4、线程池有哪几种?

1)FixedThreadPool:固定线程数的线程池。该线程池创建时需要指定线程数量,一旦创建完成后,线程池中的线程数量就不会再改变了。

2)CachedThreadPool:缓存线程池。该线程池的线程数量不固定,会根据实际情况动态地创建新的线程,并且在60秒内不使用的线程会被回收,因此适用于执行短时间的异步任务。

3)SingleThreadExecutor:单线程的线程池。该线程池只有一个线程在执行任务,保证任务按照指定的顺序依次执行。

4)ScheduledThreadPool:定时任务线程池。该线程池可以按照给定的时间间隔来周期性地执行任务。

5)WorkStealingPool:工作窃取线程池。该线程池使用了一种工作窃取算法,可以让空闲线程去“窃取”其他线程的任务,从而提高线程利用率。

5、什么是ThreadLocal?

ThreadLocal 是 Java 中一个线程级别的变量,它提供了线程内部的数据存储方式。简单来说,ThreadLocal 可以让每个线程都拥有自己独立的变量副本,这些变量只有在特定线程中才能访问,不同线程之间互不干扰。

ThreadLocal 主要有两个方法:get() 和 set()。get() 方法可以获取当前线程的变量副本,而 set() 方法可以为当前线程设置一个新的变量副本。

ThreadLocal 可以解决多线程环境下数据共享的问题,同时也能够提高程序的性能。例如,在多线程的 Web 应用中,每个线程都需要访问数据库连接,使用 ThreadLocal 可以让每个线程拥有自己独立的数据库连接,从而避免了线程安全问题,并且可以减少线程之间频繁的数据库连接开关的开销,提高了程序的性能。

需要注意的是,ThreadLocal 变量只对当前线程有效,因此需要在每个使用 ThreadLocal 的线程中进行初始化。同时,由于 ThreadLocal 存储的变量是线程独有的,如果不及时清理,可能会导致内存泄漏问题,因此需要及时清理 ThreadLocal 变量的值。

6、ThreadLocal有哪些应用场景,底层是如何实现的

ThreadLocal 主要应用在多线程场景中,通常用于解决以下两个问题:

1)需要将数据传递给某个线程,并且希望该数据只在该线程内部使用,不被其他线程访问或修改。

2)需要在多线程环境下保证某些数据的线程安全性。

常见的应用场景包括:数据库连接、Session 管理、用户身份信息等。

底层实现上,每个 Thread 对象中都维护了一个 ThreadLocalMap 对象,ThreadLocalMap 中存储了当前线程中所有 ThreadLocal 对象以及对应的变量副本。ThreadLocal 对象作为 key,变量副本作为 value。当通过 ThreadLocal 的 get() 方法获取变量值时,ThreadLocal 会先获取当前线程中的 ThreadLocalMap 对象,然后以自身作为 key,从 ThreadLocalMap 中获取对应的变量副本。如果当前线程中不存在该 ThreadLocal 对象,则会通过 set() 方法创建一个新的变量副本并存储到 ThreadLocalMap 中。当变量副本不再需要使用时,应该调用 ThreadLocal 的 remove() 方法进行清理,以避免内存泄漏问题。

需要注意的是,由于 ThreadLocal 存储的变量是线程独有的,因此需要在每个使用 ThreadLocal 的线程中进行初始化,并且需要及时清理 ThreadLocal 变量的值,以避免内存泄漏问题。

7、泛型中extends和super的区别

在Java中,泛型中的 extends 和 super 是用来限制泛型类型参数的关键字。

extends 和 super 的区别如下:

extends:用于限制泛型参数的上界(upper bound),即类型参数必须是 extends 后面的类型或其子类。例如:class A {…},表示 T 必须是 Number 类型或其子类。

super:用于限制泛型参数的下界(lower bound),即类型参数必须是 super 后面的类型或其父类。例如:class A {…},表示 T 必须是 Number 类型或其父类。

8、说一下hashMap的put方法

1)计算键的哈希值(使用键对象的hashCode()方法)
2)通过哈希值计算出键值对应的桶的位置,如果桶为空,则直接将键值对存储在该位置,并返回 null。如果桶不为空,那么就遍历桶中已经存在的键值对,如果找到了键相同的键值对,则用新的值替换旧值,并返回旧值。
3)如果遍历完整个桶都没有找到键相同的键值对,则将新的键值对添加到桶的末尾,并返回 null。

9、hashMap的扩容机制

HashMap 在插入元素时,如果发现当前元素个数超过了负载因子(默认为 0.75)和当前容量的乘积,则会触发扩容操作。扩容操作会创建一个新的数组(容量为原来的 2 倍),将原有的元素重新 hash 到新数组中,并释放旧数组。

扩容的过程可以分为以下几个步骤:

1)创建一个新的数组,容量为原来的 2 倍。

2)将旧数组中的元素重新 hash 到新数组中。由于新数组的容量是旧数组的 2 倍,因此,每个元素的新下标可以通过将旧下标的二进制最高位添加一个 1 得到。

3)将新数组中的元素替换到原来的位置。由于新数组的容量是原来的 2 倍,因此,新数组中的每个桶中可能会有多个元素。如果新数组中的桶中只有一个元素,则直接存储在桶中。如果新数组中的桶中有多个元素,则采用链表或红黑树的形式存储。

4)释放旧数组。由于旧数组中的元素已经被重新 hash 到新数组中,因此,旧数组中的元素可以被释放掉了。

需要注意的是,由于扩容操作需要重新 hash 所有元素,因此,扩容的时间复杂度是 O(n) 的,其中 n 是 HashMap 中元素的个数。为了避免频繁扩容导致性能下降,通常可以在创建 HashMap 时,指定一个较大的容量和负载因子。

10、什么是红黑树

红黑树是一种自平衡的二叉搜索树,它通过对每个节点上的额外属性进行约束和旋转操作来保证树的高度始终是 O(log n) 级别的,从而使得其支持在 O(log n) 时间复杂度内完成插入、删除、查找等操作。

红黑树的节点可以是红色或黑色的,每个节点包含以下属性:

  • key:存储该节点的键值;
  • value:存储该节点的值;
  • color:存储该节点的颜色,可能是红色或黑色;
  • left:指向该节点的左子节点;
  • right:指向该节点的右子节点;
  • parent:指向该节点的父节点。

红黑树必须满足以下性质:
1)每个节点要么是红色的,要么是黑色的。
2)根节点是黑色的。
3)每个叶子节点(NIL 节点)都是黑色的。
4)如果一个节点是红色的,则其子节点必须是黑色的。
5)从任意一个节点到其每个叶子节点的所有路径都包含相同数目的黑色节点。

红黑树在插入、删除节点时,通过旋转操作和节点的颜色变换来保证树的高度始终是 O(log n) 级别的,从而保持了红黑树的平衡性质。红黑树通常用于实现高效的集合、映射等数据结构,如 Java 中的 TreeMap、TreeSet 等。

11、什么是字节码,采用字节码的好处是什么

字节码是一种中间代码,java 程序在编译之后会生成字节码文件(.class 文件),这些字节码文件可以被 JVM 解释执行或者即时编译器编译成本地机器码执行。

采用字节码的好处主要有以下几个:
1)跨平台性:字节码可以在不同平台上执行,不需要重新编译。这是因为 JVM 可以在不同的操作系统上运行,并且能够理解相同的字节码。
2)安全性:Java 的字节码是一种相对安全的代码,因为字节码的执行由 JVM 负责,可以进行许多安全检查,防止恶意代码的执行。
3)可移植性:由于字节码文件包含了程序的中间表示形式,因此在不同的 Java 虚拟机中可以使用相同的字节码文件,使得 Java 应用程序具有很好的可移植性。
4)高效性:字节码文件相对于源代码文件的大小要小得多,可以减少传输时间和存储空间,并且可以通过即时编译器将字节码转换成本地机器码,提高程序的执行效率。

总之,采用字节码的好处主要是实现了 Java 的跨平台性和安全性,并且使得 Java 应用程序具有很好的可移植性和高效性。

12、java中的异常体系是怎样的

Java 中的异常体系分为两种类型:Checked Exception 和 Unchecked Exception。

Checked Exception 是指在代码编写过程中,编译器能够检查到的异常,必须在代码中显式地处理或者在方法签名中抛出。常见的 Checked Exception 包括 IOException、SQLException 等。

Unchecked Exception 是指在代码编写过程中,编译器无法检查到的异常,也称为运行时异常(RuntimeException)。通常由程序错误引起,比如空指针异常、数组越界异常等。这些异常不需要在代码中显式地处理或者在方法签名中抛出,但是最好在代码中进行处理,以避免程序异常终止。

除了 Checked Exception 和 Unchecked Exception,Java 中还有一个 Error 类型的异常,它表示系统错误或者虚拟机错误,比如 OutOfMemoryError、StackOverflowError 等。Error 与 Exception 不同,一般情况下不应该捕获它们。

Java 的异常体系采用了继承的方式来组织各种异常。所有的异常都是从 Throwable 类派生而来的。Throwable 类有两个子类:Exception 和 Error。Exception 又有两个子类:Checked Exception 和 RuntimeException。

Java 的异常处理机制主要包括 try-catch-finally 和 throws 关键字。try 块用于捕获可能会抛出异常的代码段,catch 块用于处理异常,finally 块用于释放资源。throws 关键字用于在方法签名中声明可能抛出的异常类型,通常用于让调用者知道该方法可能会抛出哪些异常。

13、在Java的异常处理机制中,什么时候应该抛出异常,什么时候捕获异常?

在 Java 的异常处理机制中,应该在以下情况下抛出异常:
1)当出现程序无法处理的错误时,比如内存不足、文件不存在等。这些错误无法被程序本身处理,需要抛出异常让上层调用者处理。
2)当方法不能够达成预期的目标时,比如参数错误、无法连接网络等。这些情况下也应该抛出异常,让调用者知道发生了什么错误并进行处理。
3)当方法需要进行资源释放时,比如文件、数据库连接等,如果出现了异常,应该将资源释放后再抛出异常,以避免资源泄漏。

而在以下情况下应该捕获异常:
1)当程序能够处理异常并且希望继续执行时,可以捕获异常并进行处理。比如当打开一个文件失败时,程序可以选择创建一个新的文件并继续执行。
2)当程序需要对异常进行统一的处理时,可以捕获异常并进行记录、报警、重试等处理。
**3)当程序需要对外层调用者隐藏细节信息时,**可以捕获异常并抛出一个更高层次的异常,让上层调用者能够更容易地理解和处理。

总之,在 Java 的异常处理机制中,应该根据实际情况来决定何时抛出异常、何时捕获异常,以保证程序的健壮性和可靠性。

14、java中有哪些类加载器?

Java 中有三种系统提供的类加载器:

1)Bootstrap ClassLoader:也称为启动类加载器,是最顶层的类加载器,负责加载 Java 的核心类库,如 rt.jar 和 i18n.jar 等。

2)Extension ClassLoader:也称为扩展类加载器,负责加载 Java 的扩展类库,如 jre/lib/ext 目录下的 jar 包。

3)System ClassLoader:也称为应用程序类加载器,负责加载应用程序类路径上的类。通常使用 ClassLoader.getSystemClassLoader() 方法获取该类加载器的实例。

15、什么是类加载器双亲委派模型?

类加载器双亲委派模型是指在类加载的过程中,一个类加载器在加载类之前,先将请求交给它的父类加载器去尝试加载,只有在父类加载器无法加载时,才会由该类加载器自己去加载类。
具体来说,当一个类加载器接收到加载一个类的请求时,它会先向上委派该请求给它的父类加载器去尝试加载,如果父类加载器能够加载该类,则返回父类加载器加载的类,否则该类加载器会尝试自己去加载类。如果该类加载器能够加载该类,则返回该类加载器加载的类,否则继续向上委派给父类加载器。

16、java中哪些是线程共享区?

堆区和方法区是线程共享区,栈、本地方法栈、程序计数器是每个线程独有的。
堆内存(Heap):堆内存是 JVM 中所有线程共享的内存区域,用于存储对象实例和数组等数据结构。

方法区(Method Area):方法区也是 JVM 中所有线程共享的内存区域,用于存储已加载的类信息、常量、静态变量、方法等数据。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值