目录
- 前言
- 1.线程创建
-
- [1.1 继承Thread类](#1.1 继承Thread类)
- [1.2 实现Runnable接口](#1.2 实现Runnable接口)
- [1.3 使用lambda表达式](#1.3 使用lambda表达式)
- 2.线程中断
-
- [2.1 使用变量中断线程](#2.1 使用变量中断线程)
- [2.2 使用内置的标志位](#2.2 使用内置的标志位)
- 3.线程等待
- 4.线程休眠和获取线程引用
- 总结
前言
上篇文章简单介绍了进程和线程,这边文章来简述一下Java中Thread类的基本用法,在Java中使用多线程编程。
1.线程创建
1.1 继承Thread类
我们创建一个继承自Thread的内部类,同时重写Thread的run()方法这里重写run()方法是用于编写我们想要此线程所要执行的功能的代码。即线程的入口
java
//继承 Thread, 重写 run
class MyThread extends Thread{
@Override
public void run() {
System.out.println("线程运行");
}
}
而后创建示例并且启动线程
java
public class Demo1 {
public static void main(String[] args) {
//创建线程对象
MyThread t = new MyThread();
//启动线程
t.start();
}
}
注意,这里不能使用t.run()这是由于t.run()是用于执行线程,并不能创建一个新线程,虽然结果一样,但是是在主线程执行的,这里的t.start()是首先创建一个新线程,而后调用我们的run()方法,从而实现多线程编程。
此外,此代码可以使用匿名内部类来进行简化:
java
//继承 Thread, 重写 run, 使用匿名内部类
public class Demo3 {
public static void main(String[] args) {
Thread t = new Thread(){
@Override
public void run() {
System.out.println("线程运行");
}
};
}
}
1.2 实现Runnable接口
java
class MyRunnable implements Runnable{
@Override
public void run() {
System.out.println("线程运行");
}
}
可以看到和上面那个实现方法是十分相似的。
但是在main方法里却不能创建Runnable实例然后调用start,在Runnable中没有start方法,所以需要搭配Thread来启动线程。
java
public class Demo2 {
public static void main(String[] args) {
//创建runnable对象
Runnable runnable = new MyRunnable();
//创建线程对象,传入runnable对象
Thread t = new Thread(runnable);
t.start();
}
}
在创建thread对象时,将runnable实例传入。
同样 也可以使用匿名内部类:
java
//实现 Runnable, 重写 run, 使用匿名内部类
public class Demo4 {
public static void main(String[] args) {
Thread t = new Thread(new Runnable(){
@Override
public void run() {
System.out.println("线程运行");
}
});
t.start();
}
}
1.3 使用lambda表达式
在实际应用中,使用最多的就是这种方法:
java
//使用 lambda 表达式
public class Demo5 {
public static void main(String[] args) {
//使用 lambda 表达式创建线程对象
Thread t = new Thread(() ->{
System.out.println("线程运行");
});
t.start();
}
}
2.线程中断
线程启动之后,如果遇到一些问题,不想让线程继续运行了,就需要停止线程,也就是线程中断。
2.1 使用变量中断线程
创建一个boolean变量,来控制线程的终止:
java
public class Demo9 {
private static boolean flag = true;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (flag) {
System.out.println("子线程运行");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
System.out.println("主线程运行");
Thread.sleep(2000);
flag = false;
System.out.println("让t线程停止");
}
}
可以观察出,两秒后,flag变为false,子线程终止,同时运行到最后,主线程也终止。
2.2 使用内置的标志位
Thread类中本身就有一个boolean变量,通过调用isInterruptted()方法来判断,如果为false,说明线程并没有中断,如果为Ture,则说明有事件尝试将线程中断。
java
public class Demo10 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!t.isInterrupted()) {
}
});
}
}
如果直接这样调用,会出现问题,提示t未初始化,这是因为在lambda表达式中,先执行箭头右边的语句,也就是说在t初始化之前我们就执行了t中的方法,所以需要换一种写法:
java
public class Demo10 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
}
});
}
}
使用Thread.currentThread()来获取当前线程的引用。
使用interrupt()方法来尝试中断线程
java
public class Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程运行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
Thread.sleep(2000);
//尝试中断线程
t.interrupt();
}
}
运行后,发现没有成功中断进程
这是由于interrupt()可以唤醒正在"sleep"的线程,报错也显示sleep被打断,打断之后判断中断的标志位改成了false,一直满足while的条件,所以循环不会终止,一直运行。
可以这样解决:
在触发异常后break掉while语句
java
public class Demo10 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("线程运行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
break;
}
}
});
t.start();
Thread.sleep(2000);
//尝试中断线程
t.interrupt();
}
}

3.线程等待
由于多线程在运行的时候,是随机进行调度的,有时候一个线程的任务需要等另一个线程结束后,才能进行工作,这个时候就需要进行线程等待了。
线程等待能够将多个线程按照规定的顺序运行。
我们使用join()方法来实现。
java
public class Demo11 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
//循环运行五次
for (int i = 0; i < 5; i++) {
System.out.println("线程运行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
break;
}
}
});
//运行
t.start();
System.out.println("主线程运行前");
//main线程等待t线程结束
t.join();
System.out.println("主线程运行后");
}
}
我们来看看结果:
可以看到,主线程在等待t运行结束,在t线程运行结束后,主线程开始运行。所以t.join()实际上就是t线程被主线程等待。
我们也可以做到t线程等待main线程:
java
public class Demo12 {
public static void main(String[] args) {
// 获取main引用
Thread main = Thread.currentThread();
Thread t = new Thread( () -> {
try {
System.out.println("t线程等待中");
main.join();
System.out.println("t线程运行中");
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
for (int i = 0; i < 5; i++) {
System.out.println("主线程运行中");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
效果:
可以给join()方法添加一个参数:
这里的参数表示等待的最大时间(毫秒),如果时间到了,就不进行等待,直接运行。
还可以增加一个参数,表示纳秒,能够更加精确。
等待时间有一定误差,大概在十毫秒左右
4.线程休眠和获取线程引用
使用sleep()方法实现线程休眠,在上面多次提到,不再赘述
线程引用也在上面体现(Thread.currentThread())。
总结
本篇文章主要介绍了Java中Thread类的一些基本用法和概念,希望能够帮助你们更加理解Java的多线程操作。