每天知识多一点 ! 每天烦恼少一点 !
文章目录
前言
今天我们继续来了解多线程中的Thread类,本篇内容我们通过讲解以下几个内容:
- Thread的常见构造方法
- Thread的几个常见属性
- 启动一个线程------start()
- 中断一个线程
- 等待一个线程------join()
- 获取当前线程引用
- 休眠当前线程
所涉及到的知识点太多,本篇中出现很多代码。通过代码动手的编写我们能够更好的理解!!
Thread 类是 JVM 用来管理线程的一个类,换句话说,每个线程都有一个唯一的 Thread 对象与之关联。
每个执行流,也需要有一个对象来描述,而 Thread 类的对象 就是用来描述一个线程执行流 的,JVM 会将这些 Thread 对象组织起来,用于线程调度,线程管理。
1.Thread的常见构造方法
Thread t1 = new Thread();
Thread t2 = new Thread(new MyRunnable());
Thread t3 = new Thread("这是我的名字");
//在创建线程的时候,可以给线程起名字(名字是可以重复的)
//而且这种做的目的,也是方便我们之后调试
Thread t4 = new Thread(new MyRunnable(), "这是我的名字");
-------------------------------这里我们通过写一个自起名字的线程程序来更好的学习-------------------------------
这里通过 lambda 表达式创建一个线程,并且给它命名为 我的第一个测试线程
public class Demo4 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while(true){
System.out.println("Hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"我的第一个测试线程");
}
}
启动线程。开始延时打印hello
此时我们打开jdk中一个观察线程的工具jconsole.exe,我们会发现创建的线程已经成功运行起来了!
注意:我们在创建线程时所用到的 t 只是一个变量名,是在代码中存在的。而“我的第一个测试线程”才是线程的名字。而这个名字是既在代码中存在,又在程序运行中存在!!!
2.Thread的几个常见属性
- getId()获取到的ID这个是java中给Thread对象安排的身份标识,和操作系统内核中的PCB的pid,操作系统提供的线程API中的线程id都不是一回事!!身份标识可以有多个,在不同环境下,会使用不同的标识。例如
一个线程:在jvm中有一个id,在操作系统的线程API也有一个id,在内核pcb中还有一个id- getName()中的name则是在构造方法中的name
- 后台线程不会阻止线程退出,如果main等其他的前台线程执行完了,这个时候后台线程没执行完,进程也会退出。像微信步数这样的程序中我们就可以使用后台线程
- isAlive()用来判定内核的线程在不在
我们通过下面这段代码来更好的了解一下Thread的这几个常见的属性
public class Demo5 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while(true){
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"这是我的第二个测试线程");
t.start();
System.out.println(t.getId());
System.out.println(t.getName());
System.out.println(t.getPriority());
System.out.println(t.getState());
System.out.println(t.isDaemon());
System.out.println(t.isAlive());
System.out.println(t.isInterrupted());
}
}
如图所示,这里所打印出来的结果就对应着上面每一项属性
3.启动一个线程------start()
之前我们已经看到了如何通过重写 run 方法创建一个线程对象,但线程对象被创建出来并不意味着线程就开始运行了。
覆写 run 方法是提供给线程要做的事情的指令清单
线程对象可以认为是小明同学把A,B叫过来了
而调用 start() 方法,就是喊一声:”行动起来!“,这时候线程才真正独立去执行了。
这里大家要区分清楚 run() 和 start() 二者的区别:
直接调用run方法呢,并没有创建线程,只是在原来的线程中运行代码
而调用start方法,则是创建了线程,在新线程中执行代码(是和原来的线程并发的)
通过下面这段代码可以更直观的看出二者的区别
class MyThread2 extends Thread {
@Override
public void run() {
while (true) {
System.out.println("调用run方法");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static class Demo6 {
public static void main(String[] args) {
Thread t = new Thread();
t.start();
//t.run();
while (true) {
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
4.中断一个线程
A和B一旦进到工作状态,他就会按照行动指南上的步骤去进行工作,不完成是不会结束的。但有时我们需要增加一些机制,例如小明的女朋友突然来电话了,说转账的对方是个骗子,需要赶紧停止转账,那小明该如何通知A和B停止呢?这就涉及到我们的停止线程的方式了。
1.第一种方法
直接自己定义一个标志位,作为线程是否结束的标记
public class Demo7 {
private static boolean a = false;
public static void main(String[] args) {
Thread t = new Thread(()->{
while(!a){
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("t线程执行完毕!");
});
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
a = true;
System.out.println("设置了标志,让线程t结束!");
}
}
2.第二种方法
我们也可以使用标准库中自带的一个标志位
public class Demo8 {
public static void main(String[] args) {
Thread t = new Thread(()->{
while(!Thread.currentThread().isInterrupted()){
//currentThread是Thread类的静态方法,通过这个方法就可以拿到当前线程的实例
//isInterrupted是判定标志位
System.out.println("hello");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
//e.printStackTrace();
break;
}
}
System.out.println("t线程执行完毕!");
});
t.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//在主线程中,通过t.interrupt来中断这个线程,设置标志位为true
t.interrupt();
System.out.println("设置了标志,让线程t结束!");
}
}
5.等待一个线程------join()
线程之间的调度顺序是不确定的。但是我们可以通过一些特殊的操作方法,来对线程的执行顺序做出干预。
我们依然通过代码来更清楚的认识一下:
public class Demo9 {
public static void main(String[] args) {
Thread t = new Thread(() ->{
for(int i =0;i < 5;i++){
System.out.println("hello world");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
System.out.println("main 线程 join之前");
try {
t.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("main 线程 join之后");
}
}
在这里我们就能看到打印 main 线程 join 之后 这个语句是在t线程执行结束之后才进行打印的。
这里还有关于Join() 的其他几种写法,大家可以根据上述代码进行修改然后学习一下!!
6.获取当前线程引用
对于这个方法呢,我们就很熟悉了
我们依然还是上代码! ! !
public class Demo10 {
public static void main(String[] args) {
Thread thread = Thread.currentThread();
System.out.println(thread.getName());
}
}
我们可以看到当前所引用的是我们的main线程,然后通过上述代码可以将线程所引用的对象打印出来
7.休眠当前线程
这个也是我们比较熟悉一组方法,但是有一点要记得,因为线程的调度是不可控的,所以,这个方法只能 保证实际休眠时间是大于等于参数设置的休眠时间的(划重点!!)。
我们还是通过代码来具体演示:
public class Demo11 {
public static void main(String[] args) throws InterruptedException {
System.out.println(System.currentTimeMillis());
Thread.sleep(3 * 1000);
System.out.println(System.currentTimeMillis());
}
}
总结
到这里呢,我们本篇内容就结束了。通过对Thread类中我们常用到的一些构造方法,常见的属性等进行了介绍,并且每一块都附上了代码。我们通过代码的演示,能够更清楚、更详细、更深刻的了解和学习到它的精华。希望各位小伙伴们能够将代码手动敲一遍,将这些知识都容纳到自己的大脑中!
我是初学者,下期再见!!
你对他人的酸,是你前进不放弃最好的理由!!!