问题来源于多篇面经,答案都是自己手动收集整理的,有任何错漏之处欢迎指正~
进程和线程的区别?
进程是系统资源调度的最小单位;线程是CPU调度的最小单位
进程是比线程更大的概念,进程之间彼此独立,一个进程里可以包含多个线程,多线程共享一部分资源,因此彼此相互影响比多进程更大
多线程共享一部分进程的内存区域,也有自己私有的区域
进程在执行时拥有独立的内存单元,多个线程共享进程的内存,如代码段、数据段、扩展段;但每个线程拥有自己的栈段和寄存器组。
进程和线程消耗什么系统资源?
线程共享的包括:
进程ID,优先级
代码区:代码编译后的可执行机器指令。
数据区:进程地址空间中的数据区,这里存放的是全局变量
堆区:new出来的数据就存放在这个区域
线程独立拥有:
线程id:用于区分每个线程
线程优先级:用于调度
1.栈区:栈帧中保存了函数的调用嵌套、返回值、调用其它函数的参数、该函数使用的局部变量以及该函数使用的寄存器信息
2.程序计数器:接下来执行那一条指令
3.寄存器:进程运行时的计算数据,进程切换时必须保存这些数据
i++是原子性的么?怎么保证原子性?
i++不具有原子性,因为在+1之前可能其他线程改变了i的值
通过JUC中的Atomic,或者使用锁保证原子性
什么是上下⽂切换?
⼀般线程的个数都⼤于 CPU 核⼼的个数,⽽⼀个 CPU 核⼼在任意时刻只能被⼀个线程使⽤,为了让这些线程都能得到执⾏, CPU 轮流给每个线程分配时间⽚。当⼀个线程的时间⽚⽤完的时候就会重新处于就绪状态让给其他线程使⽤,这个过程就是⼀次上下⽂切换。
当前任务在执⾏完 CPU 时间⽚切换到另⼀个任务之前会先保存⾃⼰的状态,以便下次再切换回这个任务时,可以再加载这个任务的状态。 任务从保存到再加载的过程就是⼀次上下⽂切换。
如何减少上下文切换?
1.避免锁的使用。多线程竞争锁时,会引起上下文切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
2.CAS算法。Java的Atomic包使用CAS算法来更新数据,而不需要加锁。使用最少线程。
3.避免创建不需要的线程,比如任务很少,但是创建了很多线程来处理,这样会造成大量线程都处于等待状态。
4.协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换。
线程通信的方式和区别?
1.共享对象。在共享内存的并发模型里,线程之间共享程序的公共状态,线程之间通过写-读内存中的公共状态来隐式进行通信,典型的共享内存通信方式就是通过共享对象进行通信。
例如synchronized关键字进行线程同步,实现通信
2.while轮询,不断检测某个条件是否成立,比较浪费CPU资源,
此外存在可见性问题,线程都是先把变量读取到本地线程栈空间,然后再去再去修改的本地变量。因此,如果线程B每次都在取本地的 条件变量,那么尽管另外一个线程已经改变了轮询的条件,它也察觉不到,这样也会造成死循环。
3.wait/notify机制
在消息传递的并发模型里,线程之间没有公共状态,线程之间必须通过明确的发送消息来显式进行通信,在java中典型的消息传递方式就是wait()和notify()。缺点是当一个线程还未开始执行时,过早的notify是无效的。
4管道通信。就是使用java.io.PipedInputStream 和 java.io.PipedOutputStream进行通信
Java线程生命周期和切换过程
状态类型:
1.new 初始,线程被创建,未调用start()
2.runnable 运行(java将os中的就绪和运行状态统称:运行中)
3.blocked 阻塞(被锁阻塞)
4.waiting 等待(等待其他线程通知或中断,wait()进入notify()或者notifyAll()可以唤醒,a.join()会在进程a结束后唤醒)
5.time_waiting 超时等待(超过指定时间自行唤醒,sleep(),设置了Timeout参数的Object.wait(),Thread.join() )
6.terminated 终止(执行完毕或者发生异常后结束)
状态转换:
1.线程创建之后它将处于 NEW状态,调⽤ start() ⽅法后开始运⾏,进入Runnable(可运⾏) 状态。
2.当线程获得了 cpu 时间⽚(timeslice)后就处于 RUNNING(运⾏) 状态。
(os隐藏了就绪和运行状态,Java统称这两种状态为RUNNA