7天八股速记之Java后端——Day 2

发布于:2024-04-05 ⋅ 阅读:(140) ⋅ 点赞:(0)

线程和进程的区别

进程 线程
定义 操作系统进行资源分配和调度的基本单位 操作系统能够进行运算调度的最小单位
从属关系 运行程序的实例 进程的一个执行流
资源共享 进程间不能共享资源 线程间可以共享资源
上下文切换 切换速度慢 切换速度快
操纵方 操作系统 编程人员

线程和协程的区别?什么场景下用到协程?

线程和协程是并发编程中两种不同的机制,它们有一些区别:

调度方式:线程由操作系统内核进行调度,它们是操作系统的原生概念,执行的调度和切换由操作系统决定。而协程是在用户空间进行调度,程序员可以显式地控制协程的切换,而不需要依赖操作系统的调度。

并发性:线程是并发执行的,一个程序可以同时有多个线程在运行,每个线程都有自己的执行流。协程通常是单线程的,一个协程执行时可以暂停,切换到另一个协程执行,这样实现了并发。

资源消耗:线程需要较多的资源,如栈空间和线程上下文切换的开销较大。而协程通常只需要较小的栈空间,并且切换开销较低,因为协程的切换是由程序员控制的,不需要切换到操作系统内核态。

阻塞和同步:在传统的线程模型中,当一个线程遇到阻塞操作(如IO操作)时,会被操作系统调度到等待状态,从而允许其他线程运行。而协程可以通过显式的挂起和恢复操作来实现非阻塞的同步,避免了线程切换和阻塞等待。

在一些特定的场景下,协程可以提供更高效的并发编程解决方案:

高并发IO密集型应用:当应用程序需要同时处理大量IO操作(如网络请求、数据库查询等)时,使用协程可以避免线程切换的开销,提高并发处理能力。

异步编程:协程可以使异步代码的编写更加简洁和可读。通过使用协程库或语言提供的异步/await等关键字,可以编写顺序化、可读性高的异步代码,而无需显式地处理回调函数和线程切换。

有限状态机:协程可以用于实现有限状态机(FSM)的编程模型。通过使用协程来表示状态和状态转换,可以简化复杂的状态逻辑和事件处理。

需要注意的是,协程并非在所有情况下都是最佳选择。在某些情况下,如CPU密集型任务或需要与底层系统紧密交互的情况下,线程模型可能更为适合。在选择使用协程时,需要根据具体的应用场景和需求进行评估和权衡。

怎么理解容器的线程安全与线程不安全?里面具体做了什么样的实现?

  • 线程安全
    在拥有共享数据的多条线程并行执行的程序中,代码可以通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据错误等意外情况,称为线程安全。
  • 线程不安全:不提供机制保护,出现多个线程先后更改数据造成得到脏数据的可能。
    不提供机制保护,出现多个线程先后更改数据造成得到脏数据的可能。
  • 线程安全里面具体做了什么样的实现
    1.互斥同步
    通过 synchronized 关键字编译后,在同步块的前后生产 monitorenter 和 monitorexit 两个字节码指令,需要明确的 reference 对象进行解锁。获取 reference 对象后当前线程可以操作代码块,当锁的持有数归 0 锁释放后,可以在被其他线程获取。
    2.非阻塞同步
    对互斥同步的优化,对于获取锁失败的线程,将不再让其挂起,而是自旋等待一段时间,若还剩无法获取锁才将其挂起。

Java 如何实现线程安全

1.使用 Atomic 类,通过 CAS 保证操作不会在执行过程中被中断。

2.使用 synchronized、volatile 进行加锁。
需要对集合容器内部的方法进行加锁,以 map 为例,由于 hashMap 内部没有锁,所以它会导致线程不安全。而如果我们使用 hashTable,由于其中使用 synchronized 关键字进行了加锁,就可以保证线程安全。

3.使用 TLS 避免资源竞争,提供线程的副本进行同时访问和维护。

并发类库提供的线程池实现有哪些?

  1. newCachedThreadPool()
  2. newFixedThreadPool(int nThreads)
  3. newSingleThreadExecutor()
  4. newSingleThreadScheduledExecutor() 和newScheduledThreadPool(int corePoolSize)
  5. newWorkStealingPool(intparallelism)

线程池中的几个参数,比如核心线程数、最大线程数,如果让你定,你会怎么样去设定这些值?基于什么样

一般多线程执行的任务类型可以分为 CPU 密集型和 I/O 密集型,根据不同的任务类型,我们计算线程数的方法也不一样。
假设 N 为 CPU 核心数,WT 为线程等待时间,ST 为线程时间运行时间

  • CPU 密集型任务:主要消耗 CPU 资源,可以额外设置一个线程,一旦任务暂停,CPU 就会处于空闲状态,额外的线程可以充分利用 CPU的空闲时间,线程数为 N+1
  • I/O 密集型任务: I/O 交互的处理消耗较大,而线程在处理 I/O 的适合不会占用 CPU 来处理,因此在可以多配置一些线程,线程数2N。
  • 在一般的业务应用场景中,我们可以用下列公式计算线程数: 线程数 = N * (1 + WT / ST)

我们可以根据实际业务场景,从 “N+1” 和 “2N” 中选出一个适合的,计算出一个大概的线程数,之后通过实际压测进行增大或减小线程数调整,然后观察具体的运行时间变化,最终确定一个具体的线程数。


网站公告

今日签到

点亮在社区的每一天
去签到