提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
- 前言
- [1. 线程](#1. 线程)
-
- [1.1 创建线程的方法](#1.1 创建线程的方法)
- [1.2 构造方法](#1.2 构造方法)
- [1.3 属性](#1.3 属性)
- [1.4 启动⼀个线程 - start()](#1.4 启动⼀个线程 - start())
- [1.5 中断⼀个线程,终止一个线程,让一个线程结束](#1.5 中断⼀个线程,终止一个线程,让一个线程结束)
- [1.6 等待⼀个线程 - join()](#1.6 等待⼀个线程 - join())
- [1.7 获取当前线程引⽤](#1.7 获取当前线程引⽤)
- [1.8 休眠当前线程](#1.8 休眠当前线程)
- [2. 线程的状态](#2. 线程的状态)
- 总结
前言
1. 线程
1.1 创建线程的方法

sql
class MyTread extends Thread {
@Override
public void run() {
System.out.println("hello");
}
}
sql
MyTread myTread = new MyTread();
//创建线程
myTread.start();

调用start,就会进入进程内部,创建一个新的线程,新的线程就会执行run方法
run方法并没有去调用,但是执行了------》run就是回调函数,自己写好run,别的方法start来调用
这个代码执行,有一个进程,两个线程
调用main方法的线程就是主线程
myTread.start();就是创建了一个新的线程,和主线程是并发执行的
sql
class MyTread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
}
}
}
public class test {
public static void main(String[] args) {
MyTread myTread = new MyTread();
//创建线程
myTread.start();
while (true) {
System.out.println("hello main");
}
}
}

sql
MyTread myTread = new MyTread();
//创建线程
myTread.run();
while (true) {
System.out.println("hello main");
}

myTread.run();并没有创建新的线程,所以主线程就进入到了run方法里面去了
sql
class MyTread extends Thread {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
这里只能try,catch,针对异常的话,为什么呢
因为父类的run方法后面没有throws对应异常
子类重写父类就不能修改方法
所以就只能try catch
sql
public class test {
public static void main(String[] args) throws InterruptedException {
MyTread myTread = new MyTread();
//创建线程
myTread.start();
while (true) {
Thread.sleep(1000);
System.out.println("hello main");
}
}
}
多个线程之间,谁先去CPU上执行,这个是不确定的,随机的
现在我们通过第三方工具来直观的看两个线程之间的关系

点开idea的项目结构

点击SDK编辑

找到JDK的路径

bin目录----》二进制目录,可执行程序目录
javac.exe---》编译器
java.exe--->运行java程序的jvm
点击jconsole.exe

这个会显示本地java的进程
点击303312,就是我们刚刚写的进程
然后点击不安全连接


选择线程

左下角就是java进程中的所有进程
发现一个进程里面不只是两个线程
main是主线程
Thread-0就是我们刚刚创建的线程

点击线程可以看到详细信息
堆栈跟踪:
java.base@17.0.14/java.lang.Thread.sleep(Native Method)
app//com.ck.demo.demo1.test.main(test.java:24)
这个是调用栈
描述了函数方法之间的调用关系

其他的线程就是辅助的线程
比如垃圾回收的线程,合适的时机,释放不使用的对象
比如统计信息,调试信息的线程
我们通过idea内置的调试器也可以看到线程信息
调试运行

创建线程的方法:继承Thread,重新run
第二个就是实现Runnable,重新run
sql
class MyThread2 implements Runnable {
@Override
public void run() {
while (true) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
sql
MyThread2 myThread2 = new MyThread2();
Thread t2 = new Thread(myThread2);
t2.start();
while (true) {
System.out.println("hello main");
}
通过Thread来创建线程
线程要执行的任务,是通过Runnable来描述的,而不是通过Thread自己来描述的
Runnable只是一个任务,并不一定和线程是强相关的,后续执行任务,可以是线程,也可以是其他东西(虚拟线程,线程池)
轻量级线程---》协程,虚拟线程
第三种方法
继承Thread,重写run,通过匿名内部类来实现
sql
Thread t = new Thread() {
@Override
public void run() {
System.out.println("hello thread");
}
};
t.start();
匿名内部类没有类名,定义在其他类里面
new Thread(){},这个就是定义匿名内部类,这个类是Thread的子类,然后重写了Thread这个父类的run方法,还创建了一个子类的实例,赋值给了t
一次性使用的类,用完就丢了
然后是第四种写法
sql
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("hello thread2");
}
};
Thread t = new Thread(runnable);
t.start();

然后是第五个写法,Lambda
sql
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello thread3");
}
});
t.start();
while (true) {
System.out.println("hello main");
}

我们采取Lambda
1.2 构造方法

sql
Thread t = new Thread(()->{
System.out.println("hello thread4");
},"自定义线程");
t.start();
这个就是定义线程的名字了
最后一个构造方法,是通过线程组的方式,把多个线程放在一个组里面,方便统一设置---》用的少
1.3 属性

Id是Jvm自动分配的,不能手动设置
就是Thread对象的id
sql
Thread t = new Thread(()->{
System.out.println("hello thread");
},"自定义线程");
t.start();
System.out.println("线程id"+t.getId());;
System.out.println("线程name"+t.getName());;
System.out.println(t.getState());
System.out.println(t.getPriority());

sql
Thread t = new Thread(()->{
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
},"自定义线程");
t.start();
System.out.println("线程id"+t.getId());;
System.out.println("线程name"+t.getName());;
System.out.println(t.getState());
System.out.println(t.getPriority());

后台线程:这个线程在执行中,不能阻止进程结束,进程结束了,代表这个线程也结束了,也叫做守护线程
前台线程:如果某个线程在执行过程中能够阻止进程结束

sql
Thread t = new Thread(()->{
while (true) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"自定义线程");
t.start();
这个主线程虽然结束了,但是子线程没有结束,所以进程也没有结束
sql
Thread t = new Thread(()->{
while (true) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"自定义线程");
t.setDaemon(true);
t.start();
设置为后台线程,就不能阻止进程结束了,所以其他线程结束了,进程就结束了

main线程也是前台线程,所有前台线程结束,进程才会结束
然后进程结束了,这个后台线程也结束了

Thread对象和线程不是一个东西,线程结束了,但是Thread对象还有
sql
Thread t = new Thread(()->{
while (true) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
},"自定义线程");
System.out.println(t.isAlive());
t.start();
System.out.println(t.isAlive());

isAlive就是看这个线程有没有创建成功
1.4 启动⼀个线程 - start()
run():描述线程要执行的任务,线程的入口
start:在系统内核中创建线程(创建PCB,加入到链表中),速度比较快,然后执行线程run方法
任何线程都可以创建其他线程
一个Thread对象只能调用一下start

一个Thread对象只能对应一个线程,所以就只能一次start

IllegalThreadStateException意思就是状态不合法
1.5 中断⼀个线程,终止一个线程,让一个线程结束
就是结束到一半,强制结束了



事实final就是,没有final修饰,也没有对变量进行修改的变量

所以这样不会报错
这个就是java的特殊限制

为什么这样就可以呢

sql
Thread t = new Thread(()->{
//先获取线程的引用
Thread currentThread = Thread.currentThread();
while (!currentThread.isInterrupted()) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
t.interrupt();
isInterrupted是看是否被终止
t.interrupt();是终止线程
currentThread是获取调用这个方法的线程
Lambda里面的代码,不能访问t的,因为Lambda的代码先执行
sql
Thread t = new Thread(()->{
//先获取线程的引用
Thread currentThread = Thread.currentThread();
while (!currentThread.isInterrupted()) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();

这个是因为sleep(1000)的时候,直接就interrupt了-----》Thread.sleep直接就被唤醒了,然后抛出了InterruptedException异常,由于Thread.sleep抛出的
sql
Thread t = new Thread(()->{
//先获取线程的引用
Thread currentThread = Thread.currentThread();
while (!currentThread.isInterrupted()) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// throw new RuntimeException(e);
System.out.println("异常");
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();

为什么会这样呢
这个是因为sleep等阻塞函数被唤醒之后,就会清空interrupted标志位
sql
Thread t = new Thread(()->{
//先获取线程的引用
Thread currentThread = Thread.currentThread();
while (!currentThread.isInterrupted()) {
System.out.println("hello thread4");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// throw new RuntimeException(e);
System.out.println("异常");
break;
}
}
});
t.start();
Thread.sleep(3000);
t.interrupt();

这样就可以了


1.6 等待⼀个线程 - join()

sql
Thread t = new Thread(()->{
for (int i = 0; i < 3; i++) {
System.out.println("hello thread");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t.start();
t.join();//等待t线程结束
System.out.println("hello main");

如果t线程等待主线程到t.join()已经结束了,那么就什么都不用等了
sql
Thread t1 = new Thread(()->{
for (int i = 0; i < 3; i++) {
System.out.println("hello 1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
Thread t2 = new Thread(()->{
for (int i = 0; i < 3; i++) {
System.out.println("hello 2");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
t1.join();//等待t线程结束
t2.start();
t2.join();
System.out.println("hello main");

sql
t1.start();
t2.start();
t1.join();//等待t线程结束
t2.join();

t1.join()就是一直等待t1结束,死等

1.7 获取当前线程引⽤
sql
Thread mainThread = Thread.currentThread();
Thread t1 = new Thread(()->{
try {
mainThread.join();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
for (int i = 0; i < 3; i++) {
System.out.println("hello 1");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
t1.start();
Thread.sleep(3000);
System.out.println("hello main");
这个就是子线程等待主线程

1.8 休眠当前线程


2. 线程的状态
NEW: 安排了⼯作, 还未开始⾏动
• RUNNABLE: 可⼯作的. ⼜可以分成正在⼯作中和即将开始⼯作.
• BLOCKED: 这⼏个都表⽰排队等着其他事情
• WAITING: 这⼏个都表⽰排队等着其他事情
• TIMED_WAITING: 这⼏个都表⽰排队等着其他事情
• TERMINATED: ⼯作完成了.
sql
Thread t1 = new Thread(()->{
});
System.out.println(t1.getState());
t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

new是线程还没创建
TERMINATED是线程已经没了
RUNNABLE:正在运行,或者随时可以运行
sql
Thread t1 = new Thread(()->{
while (true) {
}
});
System.out.println(t1.getState());
t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

BLOCKED:锁竞争引起的阻塞
WAITING:join,没有超时时间的等待
TIMED_WAITING:sleep,超时时间的等待
都是阻塞等待
sql
Thread t1 = new Thread(()->{
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
System.out.println(t1.getState());
t1.start();
Thread.sleep(1000);
System.out.println(t1.getState());

sql
Thread mainThread = Thread.currentThread();
Thread t1 = new Thread(()->{
System.out.println(mainThread.getState());
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
});
// System.out.println(t1.getState());
t1.start();
t1.join();
