进程:一个内存中运行的应用程序,每个进程都有一个独立的内存空间。
线程:进程中的一个执行路径,共享一个内存空间,线程之间可以自由切换,并发执行. 一个进程最少有一个线程。线程实际上是在进程基础之上的进一步划分,一个进程启动之后,里面的若干执行路径又可以划分成若干个线程。
A: 程序计数器记录了该线程的当前执行状态。
A: 虚拟机栈和本地方法栈是线程用来保存自己操作现场的,为了保证线程自己的局部变量不被其他线程访问,所以必须私有。
稍微解释一下这个图:每个线程操作变量的流程大致为:通过JMM从主内存将变量拷贝副本到线程内,然后在线程自己的内存中进行操作,然后再通过JMM同步回主内存。(Tips:这里其实就蕴含了volatile的原理)
所有线程轮流使用 CPU 的使用权,平均分配每个线程占用 CPU 的时间。
优先让优先级高的线程使用 CPU,如果线程的优先级相同,那么会随机选择一个(线程随机性),Java使用的为抢占式调度。
CPU使用抢占式调度模式在多个线程间进行着高速的切换。对于CPU的一个核心而言,某个时刻,只能执行一个线程,而CPU的在多个线程间切换速度相对我们的感觉要快,看上去就是在同一时刻运行。 其实,多线程程序并不能提高程序的运行速度,但能够提高程序运行效率,让CPU的使用率更高。
同步: 排队执行 , 效率低但是安全.
异步: 同时执行 , 效率高但是数据不安全.
并发: 指两个或多个事件在同一个时间段内发生。
并行: 指两个或多个事件在同一时刻发生(同时发生)。
每个线程执行过程中所调用的方法都在该线程的内存空间(线程自己的栈空间)里运行。
每个线程都拥有一份自己独有的栈空间,线程与线程之间共用一份堆内存。
Java中除了long和double,其他基础类型的赋值都是原子性的。
Java中++和--操作不是原子性的。
想要防止死锁的话,在一个可能会调用锁的区域,就不要再写另一个可能会调用锁的操作。
1, new 2, runnable 3, blocked 4, waiting 5, time_waiting 6, terminal
值得强调的是:Runnable状态是一个就绪的状态,并不是正在竞争锁,而是目前没有调度到该线程
进入Terminated状态有三种可能:1、正常结束2、程序异常3、JVM异常关闭。
a. volatile的本质是在告诉JVM当前变量在寄存器(工作内存)中的值是不确定的,需要从主内存中读取;synchronized则是锁定当前变量,只有当前线程可以访问该变量,其他线程被阻塞住。
b. volatile只能用在变量级别;synchronized可以使用在变量、方法和类级别。
c. volatile只能实现变量的修改可见性,不能保证原子性;synchronized则可以保证变量的修改可见性和原子性。
d. volatile不会造成线程的阻塞;synchronized可能会造成线程的阻塞。
e. volatile标记的变量不会被编译器优化;synchronized标记的变量可以被编译器优化。
Atomic操作的底层实现利用的是CAS机制,CAS = Compare And Swap,比较并交换
CAS 机制当中使用了3个基本操作数:内存地址V,旧的预期值A,要修改的新值B。更新一个变量的时候,只有当变量的预期值A和内存地址V当中的实际值相同时,才会将内存地址V对应的值修改为B
Synchronized是悲观锁,这种线程一旦得到锁,其他需要锁的线程就挂起的情况就是悲观锁。
CAS操作的就是乐观锁,每次不加锁而是假设没有冲突去完成某项操作,如果因为冲突而失败就重试,知道成功为止。因为失败时线程会不断循环重试,所以又称为自旋锁。
Synchronized 关键字会让没有得到锁资源的线程进入BLOCKED状态,而后在争夺到锁资源后恢复为RUNNABLE状态,这个过程中涉及到的操作系统用户模式和内核模式的转换,代价比较高。
a. Wait 与 notify 提供对同一个锁对象的线程之间更精细的同步操作。
b. 使用wait(), notify() 时首先需要对调用对象加锁。
c. 调用 wait() 方法后,线程状态会从RUNNING 变为 WAITING ,并将当前线程加入到 lock 对象的等待队列中。
d. 其他某个锁住同一对象的线程,在调用 notify() 后,会将等待队列中的一个线程移到 lock 对象的同步队列,被移动的线程状态由BLOCKED 变为 RUNNABLE
e. 被移动到同步队列中的线程,必须要等到 notify() 的调用者将锁释放,才有机会从同步队列返回。这里只是有机会,因为锁释放后,等待线程会出现竞争,只有竞争到该锁的线程才会从 wait() 方法返回,其他的线程只能继续等待。
(注:针对初学者,对 d、e两条的附加解释:
假如一个线程a0调用了notifyAll(),然后会发生的事情是 blocked中所有锁对象相同的线程全部进入同步队列,他们的状态也变成了runnable 然后等到a0释放了锁资源后,同步队列中要竞争这个锁对象的线程开始竞争 最后在同步队列中没竞争到锁的,就从runnable挂起到blocked了。竞争到的就进入running )
sleep是线程类(Thread)的方法,导致此线程暂停执行指定时间,给执行机会给其他线程,但是监控状态依然保持,到时后会自动恢复。调用sleep不会释放对象锁。 wait是0bject类的方法,对此对象调用wait方法导致本线程放弃对象锁,进入等待此对象的等待锁定池,只有针对此对象发出notify方法(或notifyAll)后本线程才进入对象锁定池准备获得对象锁进入运行状态。