进程:操作系统(OS)分配资源的最小单位,OS调度的单位之一
线程:OS调度的最小单位(独立调度的基本单位)
进程和线程的关系:
一个进程有多个线程(至少有一个线程),一个线程只能属于一个进程。
资源分配给进程,同一进程的所有线程共享该进程的所有资源
线程在执行过程中,需要协作同步,不同进程的线程间要利用消息通信的办法实现同步
处理机分给线程,真正正在处理机上运行的是线程
线程是指进程内的一个执行单元,也是进程内的可调度实体
进程与线程的区别:
调度:线程作为调度和分配的基本单位,进程作为拥有资源的基本单位。
并发性:不仅进程之间可以并发执行,同一进程的多个线程之间也可以并发执行
拥有资源:进程是拥有资源的一个独立单位,线程不拥有系统的资源,但可以访问隶属于进程的资源
进程有独立的地址空间,进程崩溃后,在保护模式下不会对其他的进程产生影响,而
线程只是一个进程中的不同的执行路径。 线程有自己的堆栈和局部变量, 但线程之间没有单
独的地址空间, 一个线程死掉就等于整个进程死掉, 所以多进程的程序要比多线程的程序健
壮,但是在进程切换时,耗费的资源较大,效率要差些
进程调度算法
批处理系统:批处理系统没有太多的用户操作,在该系统中,调度算法目标是保证吞吐量和周转时间(从提交到终止的时间)。
先来先服务 (FCFS)
短作业优先(SJF)
最短剩余时间优先(SRTN)
交互式系统:交互式系统有大量的用户交互操作,在该系统中调度算法的目标是快速地进行响应。
时间片轮转:将所有就绪进程按 FCFS 的原则排成一个队列, 每次调度时, 把 CPU 时间分配给队首进程,该进程可以执行一个时间片。当时间片用完时,由计时器发出时钟中断,调度程序便停止该进程的执行,并将它送往就绪队列的末尾,同时继续把 CPU 时间分配给队首的进程。
时间片轮转算法的效率和时间片的大小有很大关系:
因为进程切换都要保存进程的信息并且载入新进程的信息, 如果时间片太小, 会导致进程切
换得太频繁,在进程切换上就会花过多时间。
而如果时间片过长,那么实时性就不能得到保证。
优先级调度:为每个进程分配一个优先级,按优先级进行调度。为了防止低优先级的进程永远等不到调度,可以随着时间的推移增加等待进程的优先级。
多级反馈队列
进程间通信方式:
管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系
命名管道 FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信
消息队列 MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。 消息队列克服了信号传递信息少、 管道只能承载无格式字节流以及缓冲区大小受限等缺点
共享存储 SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。 它往往与其他通信机制, 如信号量,配合使用,来实现进程间的同步和通信。
信号量 Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制, 防止某进程正在访问共享资源时, 其他进程也访问该资源。 因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
进程状态
操作系统分配的进程空间?线程能共享哪些?
一个进程中的所有线程共享该进程的地址空间,但他们有独立(私有)的栈,Windows 线程的缺省堆栈大小为 1M
堆:共有的空间,分全局堆和局部堆。全局堆就是所有没有分配的空间,局部堆就是用户分配的空间。堆在操作系统对进程初始化的时候分配, 运行过程中也可以向系统要额外的堆,但是记得用完了要还给操作系统,要不然就是内存泄漏。
栈:线程独有,保存期运行状态和局部变量的。栈在线程开始的时候初始化,每个线程的栈互相独立,因此线程安全。
线程安全:理想中程序的运行结果100%符合我们编写代码的预期
不安全的原因:
原子性:基本数据类型的访问都具备原子性, 例外就是 long 和 double, 虚拟机将没有被 volatile 修饰的 64 位数据操作划分为两次 32 位操作。
内存可见性:不做任何保护的情况,一个线程看到变量的值,不保证是最新值
线程的操作,必须先把数据先从主内存加载到工作内存中,一切修改必须在工作内存中进行。直到运行结束(或者主动刷新工作内存),否则,结果不会同步回主内存中。
可见性指当一个线程修改了共享变量时,其他线程能够立即得知修改。JVM 通过在变量修改后将值同步回主内存, 在变量读取前从主内存刷新的方式实现可见性, 无论普通变量还是volatile 变量都是如此,区别是 volatile 保证新值能立即同步到主内存以及每次使用前立即从主内存刷新。除了 volatile 外,synchronized 和 final 也可以保证可见性。同步块可见性由"对一个变量执行 unlock 前必须先把此变量同步回主内存,即先执行 store 和 write"这条规则获得。final的可见性指:被 final 修饰的字段在构造方法中一旦初始化完成,并且构造方法没有把 this引用传递出去,那么其他线程就能看到 final 字段的值。
代码重排序:
我们语句的执行顺序,不一定是我们书写语句的顺序。
基本底线:保证单线程情况下,运行结果符合没有重排序的结果
为什么要代码重排序:程序员书写的顺序,很可能不是解决问题的最优办法
谁在进行重排序? 编译器->JVM->CPU
代码重排序会出现什么问题?在多线程环境下,无法完全符合我们的预期
线程不安全
ArrayList / LinkedList / PrioriyQueue / HashMap / TreeMap
StringBuilder / Connenction
线程安全
Staack / StringBuffer
ArrayList不安全的原因: add方法中,有对共享数据(至少size)的修改操作,同时操作还不是原子的
JVM Runtime Memory Area
PC (线程私有)
栈 Java栈 / native 栈 (线程私有)
堆 (线程间共享)
方法区 (线程间共享)
运行时常量池 (线程间共享)
栈 -> 栈帧 -> 局部变量 (线程私有)
堆 -> 对象 -> 属性 (共享)
方法区 -> 类 -> 静态属性 (共享)