本文主要是介绍7天八股速记之Java后端——Day 2,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
线程和进程的区别
进程 | 线程 | |
---|---|---|
定义 | 操作系统进行资源分配和调度的基本单位 | 操作系统能够进行运算调度的最小单位 |
从属关系 | 运行程序的实例 | 进程的一个执行流 |
资源共享 | 进程间不能共享资源 | 线程间可以共享资源 |
上下文切换 | 切换速度慢 | 切换速度快 |
操纵方 | 操作系统 | 编程人员 |
线程和协程的区别?什么场景下用到协程?
线程和协程是并发编程中两种不同的机制,它们有一些区别:
调度方式:线程由操作系统内核进行调度,它们是操作系统的原生概念,执行的调度和切换由操作系统决定。而协程是在用户空间进行调度,程序员可以显式地控制协程的切换,而不需要依赖操作系统的调度。
并发性:线程是并发执行的,一个程序可以同时有多个线程在运行,每个线程都有自己的执行流。协程通常是单线程的,一个协程执行时可以暂停,切换到另一个协程执行,这样实现了并发。
资源消耗:线程需要较多的资源,如栈空间和线程上下文切换的开销较大。而协程通常只需要较小的栈空间,并且切换开销较低,因为协程的切换是由程序员控制的,不需要切换到操作系统内核态。
阻塞和同步:在传统的线程模型中,当一个线程遇到阻塞操作(如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 避免资源竞争,提供线程的副本进行同时访问和维护。
并发类库提供的线程池实现有哪些?
- newCachedThreadPool()
- newFixedThreadPool(int nThreads)
- newSingleThreadExecutor()
- newSingleThreadScheduledExecutor() 和newScheduledThreadPool(int corePoolSize)
- 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” 中选出一个适合的,计算出一个大概的线程数,之后通过实际压测进行增大或减小线程数调整,然后观察具体的运行时间变化,最终确定一个具体的线程数。
这篇关于7天八股速记之Java后端——Day 2的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!