JUC全文
用户态和内核态
- 用户态,用户的应用程序
- 内核态,调用内核的系统调用
系统调用,操作系统最小的功能单位
shell,特殊的应用程序,下通系统调用,上通用户应用,来连接各个小功能的程度,通过shell脚本,可以调用系统调用- 当用户应用,涉及到需要调用
系统调用的时候,就会切换为内核态,消耗内核资源
线程的实现
内核线程
- Kernel-Level Thread,KLT
- 由内核完成线程切换,内核通过调度器对线程进行调度,并将线程任务映射到各个处理器上
- 程序通过使用轻量级进程,
Light Weight Process,LWP(实际意义上的线程),来调用内核线程 - 优点,每个
LWP都是独立的调度单元,一个LWP被阻塞,并不会影响其他LWP - 缺点,基于
KLT,耗费资源,线程的创建和同步都需要系统调用,进行频繁的用户态/内核态切换
用户线程
- User Thread,UT
- 广义,非内核线程,包括
LWP(需要调用内核线程来完成) - 狭义,建立在用户控件的线程库上,内核不能感知这些线程的实现,只有当前用户进程能够管理这些用户线程
- 优点,用户线程的创建、同步、销毁和调度完全在用户态,不需要内核
- 缺点,需要用户控制实际的线程调度,如何发挥多处理器系统的特点,将多线程映射到其他处理器
线程的转换
- 线程状态
public enum State {
//新建
NEW,
//准备就绪
RUNNABLE,
//阻塞
BLOCKED,
//不见不散,
WAITING,
//过时不候
TIMED_WAITING,
//终结
TERMINATED;
}
New
- 新建状态,但还不是一个真正的线程
Thread thread = new Thread();
Runnable
- 开始真正创建一个线程,开始运行,并加入到线程组
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread());
}
});
thread.start();
- 真正调用内核方法
start0,真正创建一个线程pthread_create,与内核线程进行对应
private native void start0();
- 可以细分为
Ready/Running
区别为,是否等待到了资源并开始运行
Terminated
- 一个线程执行完毕
- 调用
thread.stop();,此方法已被抛弃 - 线程状态不可逆,无法再复活
wait方法
- 锁对象来调用
- 线程状态为
WAITING
this.wait();
- 线程状态为
TIMED_WAITING
this.wait(1000);
Thread.sleep(1000)
底层实现
- 如果锁对象为偏向锁,那么必须膨胀成重量级锁,才能进行
wait,因为要调用锁对象中的monitor的wait方法 - 将当前线程封装成
ObjectWaiter对象,加入锁对象的monitor中的waitSet队列末尾 - 释放锁对象,通过底层的
park方法挂起 wait方法实际上是一个循环,当时间到了/中断/notify会进行唤醒
notify
- 唤醒线程重新争抢锁,从哪里
wait,就从哪里被notify - 实际上,
notify只是将线程从waitSet队列队首取出,放入cxq/EntryList中,直到锁对象exit,根据唤醒策略进行获得锁
- 放入
EntryList队首- 放入
EntryList队尾EntryList为空,直接放到EntryList,放入cxq队首- 放入
cxq队尾
notifyAll实际上是遍历waitSet唤醒所有线程
Blocked
- 针对
synchronized当线程在等待锁的释放时候,会进入阻塞的状态
小结
Blocked线程一定在EntryList/cxq队列中Waiting/Timed_waiting线程,有可能是执行了park和sleep,不一定在waitSet中,只有执行了wait,才会被加入waitSet
与进程的区别
- 最小单位
- 进程是资源分配的最小单位
- 线程是CPU调度的最小单位
- 内存
- 进程有自己独立的内存空间
- 线程共享内存,JVM中在堆空间有私有的TLAB
- 一个程序至少有一个进程,一个进程至少有一个线程
- 一个线程可以创建和撤销另一个线程,同一个进程中的多个线程可以并发执行