目录
线程和方法调用栈的关系
线程中最常见的属性
1.id
2.name
3.在Java代码中看到的线程状态
4.前台线程与后台线程/精灵线程(daemon)/守护线程
JDK中自带的观察线程的工具
TimeUnit.SECONDS.sleep()
Thread.join()方法
Thread常用的几个静态方法
1.Thread.sleep()
2.Thread.current()
线程内容总结
栈和栈帧
线程和方法调用栈的关系
每个线程都有自己独立的调用栈
由于每个线程都是独立的执行流,A在调用过哪些方法,和B根本没关系。表现为每个线程都有自己的独立的栈
调用的是用一个方法:说明执行的是同一批指令
栈不同(帧不同):说明执行指令时,要处理的数据是不同
线程中最常见的属性
1.id
本进程(JVM进程)内部分配的唯一的id只能get不能set
public class Main {
static class MyThread extends Thread{
@Override
public void run() {
System.out.println(this.getId());
}
}
public static void main(String[] args) {
Thread thread= Thread.currentThread();//当前线程
System.out.println(thread.getId());//主线程id
MyThread t1=new MyThread();
t1.start();
MyThread t2=new MyThread();
t2.start();
}
}
启动几个线程就有几个id,不会重复
2.name
默认情况下,主线程默认为main,如果没有给过名字,线程名字遵守Thread-……;第一个是Thread-0,Thread-1……
可以get也可以set
public class Main2 {
static class MyThread extends Thread {
@Override
public void run() {
System.out.println(this.getName());
}
}
public static void main(String[] args) {
// Thread thread = Thread.currentThread(); // 当前线程
// System.out.println(thread.getName());
//
MyThread t1 = new MyThread();
t1.start();
MyThread t2 = new MyThread();
t2.start();
MyThread t3 = new MyThread();
t3.start();
}
}
可以通过setName()设置,也可以通过Thread()构造方法设置
public class Main3 {
static class MyThread extends Thread {
public MyThread() {
setName("我是m");
}
// public MyThread() {
// super("mm"); // 调用父类(Thread)的构造方法
// }
@Override
public void run() {
System.out.println(this.getName());
}
}
3.在Java代码中看到的线程状态
(只能获取不能设置,状态的变更是JVM控制)
(1)理论中的状态
(2)Java代码中实际看到的状态
线程中可以get/set自己的优先级
注意:这个优先级的设置,只是给JVM一些建议,把自己提上去,但最终还是要听系统的
4.前台线程与后台线程/精灵线程(daemon)/守护线程
前台线程:一般是做一些有交互工作的
后台线程:一般是做一些支持工作的线程
例如:一个音乐播放器
1.线程响应用户点击动作(前台)
2.线程去网络上下载歌曲(后台)
我们创造出来的线程默认都是前台线程,除非修改
public class Main {
public static void main(String[] args) {
Thread t = Thread.currentThread();
System.out.println(t.isDaemon()); // 返回 true 代表是 后台(daemon)线程
}
}
setDaemon()方法 修改自己是前台还是后台线程
JVM进程什么时候才退出
1.必须要求所有前台都退出:所有的前台线程都退出了,JVM进程退出了
2.和后台线程没关系,即使后台线程还在工作,也正常退出。
JDK中自带的观察线程的工具
TimeUnit.SECONDS.sleep()
TimeUnit.SECONDS.sleep(1) 意思是1s后再继续运行 。
public class Main {
public static void main(String[] args) throws InterruptedException {
Thread.currentThread().setName("我是主线程");
while (true) {
TimeUnit.SECONDS.sleep(1);
System.out.println("你好");
}
}
}
先运行再观察
可以从
中 ,找到
打开选择向对应方法打开观察
Thread.join()方法
控制另外的线程
A线程:1.创建B线程,并启动B线程
2.等待B线程完成所有工作(B线程运行结束)
3.打印B线程已经退出了
B线程:计算一个比较耗时的任务
有无join的区别
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;
public class Main {
private static class B extends Thread {
@Override
public void run() {
// 模拟 B 要做很久的工作
try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
println("B 说:我的任务已经完成");
}
}
private static void println(String msg) {
Date date = new Date();
DateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
System.out.println(format.format(date) + ": " + msg);
}
public static void main(String[] args) throws InterruptedException {
B b = new B();
b.start();
println("A 自己先去吃饭");
// 有 join 和没有 join 的区别
b.join();
println("A 说:B 给我把钱送来了,结账走人");
}
}
这是没有join的结果。
这是有join的结果
Thread常用的几个静态方法
1.Thread.sleep()
也是让线程休眠,但以毫秒为单位
TimeUnit.SECONDS.sleep(1) ==Thread.sleep(1000)
从线程的状态的角度,调用sleep(),就是让当前线程从"运行"->"阻塞"
等待某个条件:要求时间过去之后,当条件满足时,线程从“阻塞”->“就绪”,在当线程被调度器选中时开始执行之前的指令
2.Thread.current()
Thead引用,执行一个线程对象,执行的就是在哪个线程中调用的该方法,返回哪个对象。
public class Main {
static class MyThread extends Thread{
@Override
public void run() {
printCurrentThreadAttributes();
}
}
private static void printCurrentThreadAttributes(){
Thread t=Thread.currentThread();
System.out.println(t.getId());
System.out.println(t.getName());
}
public static void main(String[] args) {
MyThread t1=new MyThread();
t1.start();
MyThread t2=new MyThread();
t2.start();
}
}
线程内容总结
(结合操作系统)
1.如何在代码中创建,启动线程
2.线程在底层的原理(OS中的线程+执行流)
3.线程结果的随机性
4.线程的常见属性(状态是重点)
5.相关工具
栈和栈帧
在没有多线程(执行流)+没有外部输入的情况下,程序的运行就是一个状态机
栈:当前执行流的当前时刻(时间停止状态时)的状态框(现实方法的调用次序)
框:栈帧(frame)装的就是运行该方法时需要的一些临时数据(主要就是具备变量)



