目录
- 前言
- 1.线程创建
- 1.1 继承Thread类
- 1.2 实现Runnable接口
- 1.3 使用lambda表达式
- 2.线程中断
- 2.1 使用变量中断线程
- 2.2 使用内置的标志位
- 3.线程等待
- 4.线程休眠和获取线程引用
- 总结
前言
上篇文章简单介绍了进程和线程,这边文章来简述一下Java中Thread类的基本用法,在Java中使用多线程编程。
1.线程创建
1.1 继承Thread类
我们创建一个继承自Thread的内部类,同时重写Thread的run()方法这里重写run()方法是用于编写我们想要此线程所要执行的功能的代码。即线程的入口
//继承 Thread, 重写 run
class MyThread extends Thread{@Overridepublic void run() {System.out.println("线程运行");}
}
而后创建示例并且启动线程
public class Demo1 {public static void main(String[] args) {//创建线程对象MyThread t = new MyThread();//启动线程t.start();}
}
注意,这里不能使用t.run()这是由于t.run()是用于执行线程,并不能创建一个新线程,虽然结果一样,但是是在主线程执行的,这里的t.start()是首先创建一个新线程,而后调用我们的run()方法,从而实现多线程编程。
此外,此代码可以使用匿名内部类来进行简化:
//继承 Thread, 重写 run, 使用匿名内部类
public class Demo3 {public static void main(String[] args) {Thread t = new Thread(){@Overridepublic void run() {System.out.println("线程运行");}};}
}
1.2 实现Runnable接口
class MyRunnable implements Runnable{@Overridepublic void run() {System.out.println("线程运行");}
}
可以看到和上面那个实现方法是十分相似的。
但是在main方法里却不能创建Runnable实例然后调用start,在Runnable中没有start方法,所以需要搭配Thread来启动线程。
public class Demo2 {public static void main(String[] args) {//创建runnable对象Runnable runnable = new MyRunnable();//创建线程对象,传入runnable对象Thread t = new Thread(runnable);t.start();}
}
在创建thread对象时,将runnable实例传入。
同样 也可以使用匿名内部类:
//实现 Runnable, 重写 run, 使用匿名内部类
public class Demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable(){@Overridepublic void run() {System.out.println("线程运行");}});t.start();}
}
1.3 使用lambda表达式
在实际应用中,使用最多的就是这种方法:
//使用 lambda 表达式
public class Demo5 {public static void main(String[] args) {//使用 lambda 表达式创建线程对象Thread t = new Thread(() ->{System.out.println("线程运行");});t.start();}
}
2.线程中断
线程启动之后,如果遇到一些问题,不想让线程继续运行了,就需要停止线程,也就是线程中断。
2.1 使用变量中断线程
创建一个boolean变量,来控制线程的终止:
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,则说明有事件尝试将线程中断。
public class Demo10 {public static void main(String[] args) {Thread t = new Thread(() -> {while (!t.isInterrupted()) {}});}
}
如果直接这样调用,会出现问题,提示t未初始化,这是因为在lambda表达式中,先执行箭头右边的语句,也就是说在t初始化之前我们就执行了t中的方法,所以需要换一种写法:
public class Demo10 {public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {}});}
}
使用Thread.currentThread()来获取当前线程的引用。
使用interrupt()方法来尝试中断线程
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语句
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()方法来实现。
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线程:
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的多线程操作。