* 程序由指令和数据组成,但这些指令要运行,数据要读写,就必须要将指令加载至CPU,数据加载至内存。在指令运行过程中还需要用到磁盘,网络等设备。进程就是用来加载指令,管理内存,管理IO的。
* 当一个程序被运行,从磁盘加载这个程序的代码至内存,这时就开启了一个进程。
* 进程可以视为程序的一个实例。大部分程序可以运行多个实例(例如:记事本、画图、浏览器等),也有的程序只能启动一个实例进程(例如:网易云音乐、360安全卫士)
* 一个进程之内可以有一到多个线程。
* 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行。
* Java中,线程是最小的调度单位,进程是资源分配的最小单位。在Windows中进程是不活动的,只是作为线程的容器。
* 进程基本上相互独立的,而线程存在于进程内,是进程的一个子集。
* 进程拥有共享的资源,如内存空间等,供其内部的线程共享。
* 进程间通信较为复杂
* 同一台计算机的进程通信称为IPC(Inter-process communication)
* 不同台计算机之间的进程通信,需要通过网络,并遵守共同的协议,例如HTTP
* 线程通信相对简单,因为他们共享进程内的内存,一个例子是多个线程可以访问同一个共享变量。
* 线程更轻量,线程上下文切换成本一般上要比进程上下文切换低。
我们都知道JVM中由堆、栈、方法区组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存。
* 每个栈由多个栈帧组成,对应这每次方法调用时所占用的内存。
* 每个线程只能有一个活动栈帧,对应这当前正在执行的方法。
当把这个TestFrames类加载进方法区后。
类加载完成后,这时候Java虚拟机就会启动一个名称叫做main的主线程,并给这个主线程分配一块线程栈内存。
程序计数器:就是表示当前执行哪一行的代码。
因为一些原因导致cpu不在执行当前的线程,转而执行另一个线程的代码。
* 线程的CPU时间片用完。
* 垃圾回收
* 有更高优先级的线程需要运行。
* 线程自己调用了sleep,yield,wait,park,synchronized,lock等方法。
当Context Switch发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器(Program Connter Register),它的作用是记住下一条jvm指令的执行地址,是线程私有的。
状态包括程序计数器,虚拟机栈中每个栈帧的信息,如局部变量,操作数栈,返回地址等。
Context Switch(上下文切换)频繁发生会影响性能。
* 调用sleep会让当前线程从Running状态进入Time Waiting状态(阻塞)。而且sleep不会释放锁。
* 其他线程可以使用interrupt方法打断正在睡眠的线程。这时sleep方法会抛出一个InterruptedException异常。
* 睡眠结束后的线程未必会立刻得到执行。(结束后会进入就绪状态)
* 建议使用TimeUnit的sleep代替Thread的sleep来获得更好的可读性。
* 调用yield会让当前线程从Running进入Runable(就绪状态),然后调度执行其他线程。(该线程仍然还有机会被调度)
* 具体的实现依赖于操作系统的任务调度器。
* 线程优先级会提示调度器优先调度该线程,但它仅仅是一个提示,调度器可以忽略它。
* 如果CPU比较忙,那么优先级高的线程会获得更多的时间片。
用于同步,在另外一个线程中等待一个线程执行结束
打断sleep,wait,join的线程。
打断sleep的线程,会清空打断状态。即打断状态为:false
在主线程中打断t1线程。但是t1是并为真的打断。所以通过Thread.currentThread()获取到当前线程。isInterrupted()这里打断正在运行的线程结果就是true。
LockSupport.park()方法,会让代码暂停不会向下执行。
interrupt就可以打断,让它继续向下运行。
但是打断标记为true后,park()就没有效果了 .park是根据打断标记来判断是否向下执行。
Thread.interrupted()会查看是否打断,并把打断标记变为false
流程图
使用volatile实现
* 初始状态:仅在语言层面创建了线程对象,还未与操作系统线程关联。
* 可运行状态:(就绪状态)指该线程已经被创建(与操作系统线程关联),可以由CPU调度执行。
* 运行状态:指已经获取了cpu
* 当cpu时间片用完,会从运行状态转换至可运行状态,导致线程的上下文切换。
* 阻塞状态:
* 如果调用了阻塞API,如BIO读写文件,这时线程实际不会用到CPU,导致线程的上下文切换,进入阻塞状态。
* 等BIO操作完成,会由操作系统唤醒阻塞的线程,转换至可运行状态。
* 与可运行状态的区别是,对阻塞状态的线程来说,只要他们一直不唤醒,调度器就一直不考虑调度他们。
* 终止状态:表示线程已经执行完毕,生命周期已经结束,不会转换为其他状态。
NEW:
RUNNABLE:调用了start()
TERMINATED:主线程有个等待sleep,所以这里的状态是TERMINATED
TIMED_WAITING:有时间等待
WAITING:没有时间的等待
BLOCKED:想要获得锁,但是获得不到锁。