线程上下文切换(Thread Context Switch)
因为以下一些原因导致cpu不再执行当前的线程,转而执行另一个线程的代码
- 线程的cpu时间片用完
- 垃圾回收
- 有更高优先级的线程需要运行
- 线程自己调用了sleep、yield、wait、join、park、synchronized、lock等方法
当 Context Switch 发生时,需要由操作系统保存当前线程的状态,并恢复另一个线程的状态,Java中对应的概念就是程序计数器,它的作用是记住下一条jvm指令的执行地址,是线程私有的
- 状态包括程序计数器、虚拟机栈中每个栈帧的信息,如局部变量、操作数栈、返回地址等
- Context Switch 频繁发生会影响性能
常见方法
- start 与 run
调用 run
public static void main(String[] args) {
Thread t1 = new Thread("t1"){
@Override
public void run(){
System.out.println("running");
}
};
t1.run();
//t1.start();
System.out.println("hello ");
}
输出
running
hello
程序的方法调用还是同步的
调用 start
将上述代码的 t1.run() 改为
t1.start()
输出
hello
running
或者
running
hello
小结
- 直接调用 run 是在主线程中执行了run, 没有启动新的线程
- 使用 start 是启动了新的线程,通过新的线程间接执行 run中的代码
- sleep 与 yield
sleep
- 调用 sleep 会让当前线程从 Running 进入 Timed Waiting 状态 (阻塞)
- 其他线程可以使用 interrupt 方法打断正在睡眠的线程,这时 sleep方法会抛出
InterruptedException
- 睡眠结束后的线程未必会立刻得到执行
- 建议用 TimeUnit 的 sleep 代替 Thread 的 sleep 来获得更好的可读性
yield
- 调用 yield 会让当前线程从 Running 进入 Runnable 就绪状态,然后调度执行其他线程
- 具体的实现依赖于操作系统的任务调度器
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread("t1"){
@Override
public void run() {
super.run();
System.out.println("enter sleep");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
System.out.println("被打断了");
e.printStackTrace();
}
}
};
t1.start();
TimeUnit.SECONDS.sleep(1);
// Thread.sleep(1000);
t1.interrupt();
}