架构师养成记-JUC
线程基础
并发与并行
并发,concurrency,是一段时间内处理多件事情的需求,它是问题域problem domain的概念。需求
并行,parallelism,是在同一时刻同时处理多件事情的方式,它是方法域solution domain的概念。 解决方案
并发是问题,是需求;并行是解决并发问题的方法之一

进程与线程
操作系统将内存分配给不同的进程,将CPU分配给不同的线程;同一个进程内有多个线程可以共享该进程分配到的内存,而CPU以线程为单位在不同线程间不断切换执行。
创建线程的方式
创建任务并加载到Thread,有三种方式:demo
- 继承Thread
- 实现Runnable
- 实现Callable 可以 使用Future获取Callable返回值 使用FutureTask获取Callable返回值
操作系统线程状态和JVM线程状态进行梳理

解释:
- 线程创建后会进入
ready就绪状态。 - 操作系统将CPU时间切分为一个一个连续的周期,比如10~20ms,称为CPU时间分片,然后按照这个分片轮转地选择就绪状态的线程去执行,被选择的线程进入
running状态。 - 当一次时间分片结束,操作系统会发出一个中断信号(interrupt),通知CPU中断当前
running状态的线程,将其退回就绪状态,并重新从就绪状态的线程中选择一个执行。即CPU切换线程。 - 当某个
running的线程执行到IO操作(比如读写磁盘)时,该线程会退出CPU,不再运行在CPU上,而是运行在磁盘设备上;从CPU的角度而言,它进入了waiting等待/阻塞状态;习惯上我们称这种状态为IO阻塞,但其实对线程而言只是执行它的地点从CPU换到了磁盘或其他IO设备上了而已。此时CPU当然会选取其他就绪线程去执行以避免CPU资源浪费。 - 当IO操作结束时,对应线程从
waiting状态变为就绪状态,操作系统也会给CPU发出一个中断信号(interrupt),通知CPU再次切换线程。
当然这5种状态只是一个抽象或者说总结,实际上不同的操作系统对线程状态的划分和命名不尽相同。比如waiting,在Linux操作系统中,就对应着S(浅度睡眠)、D(深度睡眠)、T(暂停态)等状态,总之我们将其理解为还可能再次运行的,目前因为IO阻塞等原因不占用CPU的线程状态即可 cpu视角定义线程的状态
jvm层面的线程状态
-
从
Thread.State这个枚举中可以看到Java定义的6种线程状态:- NEW
- RUNNABLE
- BLOCKED
- WAITING
- TIMED_WAITING
- TERMINATED
这段代码能够很好的说明 线程之间的状态转换
private void testReenterBlocked() {
class EchoPrinterAgain {
// synchronized修饰实例方法,则锁为 this,即EchoPrinter的一个实例
public synchronized void echoPrint1() {
Scanner scanner = new Scanner(System.in);
System.out.println("我是胡汉三,我要准备跑路了,随便说点啥吧:");
String content = scanner.nextLine();
System.out.println(content);
try {
// 让出锁,当前JAVA线程进入WAITING状态
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我胡汉三又回来了。。。over。。。");
}
public void echoPrint2() {
System.out.println("我是潘冬子,准备抢锁,按导演的计划,我会失败...");
// 使用 this 作为锁,即EchoPrinter的一个实例
synchronized (this) {
System.out.println("我是潘冬子,我抢到锁了...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是潘冬子,导演让我喊胡汉三回来...");
this.notifyAll();
System.out.println("我是潘冬子,我唤醒了胡汉三,等我休息三秒...");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("我是潘冬子,我即将退出舞台。。。");
}
}
}
// 因为锁是EchoPrinter的一个实例,这里需要先生成实例
EchoPrinterAgain echoPrinterAgain = new EchoPrinterAgain();
// 创建线程1并启动,线程1将运行echoPrint方法
Thread t1 = new Thread(echoPrinterAgain::echoPrint1);
t1.start();
// 主线程等待1秒钟,以确保线程1启动
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 创建线程2并启动,线程2将运行print方法
Thread t2 = new Thread(echoPrinterAgain::echoPrint2);
t2.start();
// 主线程每隔一秒打印一次两个子线程的状态
while (true
