线程与进程
![](http://39.101.72.240:8080/picture/幻紫.jpg)
线程与进程
进程可以看成是线程的容器
如何创建线程
- 直接继承Thread类,然后重写run
- 实现Runnable借接口,重写任务,然后创建一个线程,然后吧这个runnable对象作为构造函数的参数,不需要继承
组合优于继承这里可以使用lambada表达式
1
Thread thread = new Thread(() -> System.out.println(1), "t");
- FutrueTask实现创造线程,需要配合callable类型,有返回值
可以返回线程的执行结果 get() 可以直接把一个线程的结果传给另一个线程同时可以抛出异常
观察线程同时执行
是一个轮播
- 查询命令
- win
- tasklist | find java
- taskkill
- java
- jps(查看java的进程)
- Linux
- ps -ef | grep java
- kill pid
- win
线程运行原理
栈和栈帧,一个线程启动之后,就会分配一个栈内存,每一个栈内,线程调用一次方法就会产生一个栈帧,一个栈只能有一个活动栈帧,对应的是当前正在执行的方法
线程上下文切换
- CPU分配的时间片用完
- GC
- 有更高优先级线程需要执行
- 线程自己调用了sleep(), yield()等方法
线程上下文切换的时候,程序计数器会记住下一条指令的地址,以及需要记录每一个栈帧的信息比如局部变量,操作数栈,返回地址
切换越频繁,会影响性能,不是线程数量越多越好,当线程数目大于核数,会导致频繁切换
线程中的常见方法
start() –> 启动一个新的线程,让线程进入就绪状态
只能调一次
run() –> 新的线程启动之后,就会调用run()
join() –> 等待某一个线程运行结束
join(long n) 超时时间,最多等待多少毫秒
getId(),getName(), getPriority() 优先级/set等,getState()线程状态,isAlive()是否存活
start()&run()
调用run其实是主线程调用,而不是异步
1 | public static void main(String[] args) { |
使用start()是直接线程调用,而不是主线程调用
线程状态
1 | System.out.println(t1.getState()); |
线程刚创建出来的时候,是处于NEW的状态, 然后执行start()之后的状态是runnable
sleep() & yield()
sleep()
runnable -> timed_wating
睡眠中的线程,可以被其他线程使用这个线程的interrupt()打断
醒来的线程未必会立即执行
1 | while (ture) { |
通过sleep() 防止while true空转 导致cpu一直占用
yield()
让出当前线程的CPU使用权,当前线程从running -> runnable(就绪状态)
相当于把时间片让出来
这个方法的实现依赖与系统的任务调度器
线程优先级
数字越大表示优先级越高, 该线程会提示任务调度器, 有更多的机会分配时间片
join()
在一个线程中使用另外一个线程的join方法, 会让本线程执行到这个语句的时候, 先阻塞, 然后等待另一个线程执行完之后, 再继续运行
需要等待结果返回,就是同步,不需要等待结果返回, 就能继续运行, 就是异步
有时效的join(), 如果超过等待时间, 将不会等待这个线程, 直接往下面运行
join(Long n)
interrupt()
打断线程, 被打断之后, 线程有一个打断标记, 打断之后, 设置成真
注意: 当线程处于sleep状态的时候被打断, 会重新设置interrupt值为假,并抛出异常
isInterrupter() -> bool
对于正在sleep的线程,如果被打断之后,会抛出异常
对于正常运行的线程, 被别的线程打断之后, 被打断值为真, 由程序自己判断是否需要打断, 可以自己通过isinterrupted() 判断逻辑
关于interrupt的两阶段终止设计模式
在线程1中如何优雅的终止另外一个线程2, 需要给线程2一个料理后事的机会(释放锁等动作)
错误思路
- 直接使用stop(), 强制杀死线程
但是如果线程持有锁, 锁将不能释放,不够合理
两阶段终止模式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50public class 两阶段终止模式 {
public static void main(String[] args) throws InterruptedException {
TwoPhaseTermination twoPhaseTermination = new TwoPhaseTermination();
twoPhaseTermination.start();
Thread.sleep(3500);
twoPhaseTermination.stop();
}
}
class TwoPhaseTermination {
private Thread monitor; // 监控线程
// 启动监控线程
public void start() {
monitor = new Thread(() -> {
while (true) {
// 先拿到当前线程的对象
Thread current = Thread.currentThread();
if (current.isInterrupted()) {
System.out.println("料理后事");
break;
}
try {
Thread.sleep(1000); // 情况1: 此时被打断会抛出异常, 此时会把打断标记重新设置成假
System.out.println("执行监控记录"); // 情况2: 这个情况被打断不会抛出异常
} catch (InterruptedException e) {
// 重新设置打断标记
current.interrupt();
System.out.println(e.toString());
//throw new RuntimeException(e);
}
}
});
monitor.start();
}
// 停止监控线程
public void stop() {
monitor.interrupt();
}
}
打断park()状态线程
LockSupport.park(); // 锁住了
当打断标记为真的时候, park()就会生效
主线程与守护线程
只要有一个线程没结束, 进程就不会结束
守护线程
1 | t.setDaemon(true); // 设置t为守护线程 |
当一个线程为守护线程的时,他守护的线程结束之后, 无论守护线程是否结束, 都会立即结束
线程的状态
- 五种状态的说法
初始 -> 可运行状态 -> 运行状态 -> 终止状态
| |
阻塞状态 <– - 六种状态
根据state的枚举类划分
NEW RUNNABLE BLOCKED WAITING TIMED_WAITING TERMINATED