JUC学习笔记
JUC学习
进程与线程
进程
- 程序由指令和数据组成,数据要读写,就必须将指令加载至CPU,数据加载至内存,在指令运行过程中还需要用到磁盘,网络等设备.进程就是用来加载指令,管理内存,管理IO的
- 进程可以视为程序的一个实例,大部分程序可以运行多个实例进程,有的程序也只能启动一个实例进程
线程
- 一个进程之内可以分一道多个线程
- 一个线程就是一个指令流,将指令流中的一条条指令以一定的顺序交给CPU执行
- Java中,线程作为最小的调度单位,进程作为资源分配的最小单位
区别
- 进程相互独立,而线程存在于进程内,是进程的一个子集
- 进程拥有共享的资源,如内存空间,供其内部的线程共享
- 进程间通信较为复杂
- 线程间通信较为简单,因为他们共享进程内的内存,一个例子是多线程可以访问同一个共享变量
- 线程更轻量,线程上下文切换成本一般要比进程上下文切换低
并行与并发
- 并发是同一时间应对多件事情的能力
- 并行是同一时间动手做多件事情的能力
案例:
- 家庭主妇做饭,打扫卫生,给孩子喂奶,一个人轮流交替做,就是并发
- 家庭主妇雇了保姆,他们一起干,既有并发,又有并行
Java线程
创建和运行线程
- 方法一,直接使用Thread
1 | // 创建线程对象 |
方法二,使用Runable,配合Thread
1
2
3
4
5
6
7
8
9
10// 创建任务对象
Runnable task2 = new Runnable() {
public void run() {
log.debug("hello");
}
};
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start()1
2
3
4
5// 创建任务对象
Runnable task2 = () -> log.debug("hello");
// 参数1 是任务对象; 参数2 是线程名字,推荐
Thread t2 = new Thread(task2, "t2");
t2.start();方法三,FutureTask 配合 Thread
futureTask能够接收Callable类型的参数,用来处理有返回结果的情况
1
2
3
4
5
6
7
8
9
10// 创建任务对象
FutureTask<Integer> task3 = new FutureTask<>(() -> {
log.debug("hello");
return 100;
});
// 参数1 是任务对象; 参数2 是线程名字,推荐
new Thread(task3, "t3").start();
// 主线程阻塞,同步等待 task 执行完毕的结果
Integer result = task3.get();
log.debug("结果是:{}", result);
栈与栈帧
JVM由堆,栈,方法区所组成,其中栈内存是给谁用的呢?其实就是线程,每个线程启动后,虚拟机就会为其分配一块栈内存
- 每个栈由多个栈帧(Frame)组成,对应着每次方法调用时所占用的内存
- 每个线程只能有一个活动栈帧,对应着当前正在执行的方法
线程上下文切换
线程的cpu时间片用完
垃圾回收
有更高优先级对的线程需要运行
线程自己调用了sleep,yield,wait,join,park,synchronized,lock等方法
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java 中对应的概念
就是程序计数器(Program Counter Register),它的作用是记住下一条 jvm 指令的执行地址,是线程私有的状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
Context Switch 频繁发生会影响性能
常用方法
Sleep与yield
Sleep
- 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态(阻塞)
- 其它线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep 方法会抛出 InterruptedException
- 睡眠结束后的线程未必会立刻得到执行
- 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield
- . 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其它线程具体的实现依赖于操作系统的任务调度器
两阶段终止模式
一个线程t1如何优雅的终止线程t2? 给t2一个料理后事的机会
错误思路
使用stop()方法 停止线程
stop方法会真正的杀死线程,如果线程锁住了共享资源,那么它被杀死后无法释放锁,其他线程将永远无法获取锁
使用System.exit(int) 方法停止线程
目的仅是停止一个线程,但会让整个程序都停止
两阶段终止模式
1.利用isInterrupted
1 | class TPTInterrupt { |





