在Java中,Thread类是一个实现多线程编程的一个基础类。我们可以对线程有多种操作,比如创建线程,终止线程,休眠线程,等待线程,获取线程的实例等。下面将逐个介绍以上操作。
1. 创建线程
创建线程有多种方法,主要有:(1)继承Thread类,重写run方法;(2)实现Runnable,重写run方法;(3)继承Thread,重写run,使用匿名内部类;(4)实现Runnable,重写run,使用匿名内部类;(5)使用Lambda表达式(最常用)。
1.1 继承Thread,重写run()
public class MyThread1 extends Thread{
//继承Thread,重写run
@Override
public void run(){
System.out.println("this is thread!");
}
public static void main(String[] args) throws InterruptedException {
Thread t = new MyThread1();
t.start();//调用了系统的API,在系统内部创建了新的线程,这个线程会调用Thread类中的run方法,实现了并发编程
System.out.println("this is main!");
}
}
1.2 实现Runnable,重写run()
public class MyThread2 implements Runnable{
@Override
public void run() {
System.out.println("this is thread!");
}
public static void main(String[] args){
//实现runnable,重写run
Runnable runnable = new MyThread2();
Thread t = new Thread(runnable);
t.start();
System.out.println("this is main!");
}
}
1.3 继承Thread,重写run(),使用匿名内部类
public class MyThread3 extends Thread{
//继承Thread,重写run,使用匿名内部类
public static void main(String[] args) {
Thread t = new MyThread3(){
@Override
public void run() {
System.out.println("this is thread!");
}
};
t.start();
System.out.println("this is main!");
}
}
1.4 实现Runnable,重写run(),使用匿名内部类
public class MyThread4{
public static void main(String[] args) {
//实现runnable,重写run,使用匿名内部类
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("this is thread!");
}
};
Thread t = new Thread(runnable);
t.start();
System.out.println("this is main!");
}
}
1.5 使用Lambda表达式
public class MyThread5 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("this is thread!");
});
t.start();
System.out.println("this is main!");
}
}
以上是五种创建线程的方式,start()是线程的入口方法,t.start()调用了系统的API,在系统中创建了一个新的线程,这个线程会调用Thread的run()方法,实现了并发编程,能交替执行主线程和t线程中的输出内容。
【t.start()和t.run()的区别】
若将t.start()更换为t.run(),t.run()不会调用系统的API,也就不会创建新的线程,因此t.run()无法实现并发编程,只能一个线程一个线程的运行,只有当前线程运行完毕后才能去执行其他线程任务。
public class MyThread5 { public static void main(String[] args) { Thread t = new Thread(() -> { while (true){ System.out.println("this is thread!"); } }); t.run(); //t.start(); while (true){ System.out.println("this is main!"); } } }
运行结果是无限循环输出 this is thread!
每个线程是一个独立的执行流,t.start()可以实现并发编程,能交替运行两个线程,即能交替输出两个线程的输出内容。
public class MyThread5 { public static void main(String[] args) { Thread t = new Thread(() -> { while (true){ System.out.println("this is thread!"); } }); //t.run(); t.start(); while (true){ System.out.println("this is main!"); } } }
2. 休眠线程 和 等待线程
2.1 休眠线程
可以使用Thread类的sleep(毫秒)方法,来使一个线程休眠一定的时间,时间到之后,该线程就能继续被调度执行了
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("t线程执行中!");
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("执行完毕");
}
}
2.2 等待线程
使用Thread类的无参版本的join()方法,可让 某一个线程 等待 另一个线程执行结束后再执行,该方法也就控制着线程的执行结束的顺序。
在哪个线程中调用了join方法,则哪个线程就等待另一个线程先执行。比如 在main中调用t.jion(),则main线程就等待t线程先执行。
代码示例如下:
public class Demo3 {
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(() -> {
System.out.println("t线程执行中!");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
});
t.start();
t.join();
System.out.println("main线程执行中");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("执行完毕");
}
}
这样就可以控制线程的结束顺序,一个线程等待另一个线程执行结束后再执行。
由于线程调度是随机的,且创建一个新的线程有一定系统开销,若不使用join方法,就会先去执行main线程的任务了。
3. 终止线程 和 获取线程实例
Thread内部有一个标志位,该标志位就是用来判定当前线程是否结束,t.interrupt()就可将t线程内部的标志位设置为true,这样就可以终止t线程. 代码如下:
其中,Thread.currentThread()是用来获取当前线程的实例。
哪个线程调用了Thread.currentThread(),就返回哪个线程的对象,如下面代码中,在t线程中调用了Thread.currentThread(),则Thread.currentThread()就返回t线程的对象,也就是t。因此while循环条件的代码中Thread.currentThread()就获取到了当前线程的对象t。
【终止一个线程的两步操作】:
(1) 主线程main中 t.interrupt(),此操作就将t线程中的标志位设置为true。
(2) Thread.currentThread().isInterrupted(),此操作用来判定当前线程是否终止。
以上两步操作就成功将t线程终止了。
public class Demo2 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()){
System.out.println("t线程工作中!");
}
});
t.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t.interrupt();
System.out.println("成功终止t线程!");
}
}
若t线程中有sleep()方法,如以下代码,t.interrupt()会触发sleep内部中的一个异常,且会重置线程内部的标志位,即将刚刚设置的标志位true还原为false,这样线程就不会终止了(catch到的是e.printStackTrace()基础上)。这样的话即使调用了t.interrupt(),t线程也不会终止。这种情况就是将主动权交给我们自己,若确定要终止线程,就在catch中加上 break,就可终止t线程了。
public class Demo2 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()){
System.out.println("t线程工作中!");
try {
Thread.sleep(500);
} catch (InterruptedException e) {
//throw RuntimeException(e);
e.printStackTrace();
//break;
}
}
});
t.start();
try {
Thread.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
t.interrupt();
System.out.println("成功终止t线程!");
}
}
若catch中是throw RuntimeException(e),则会抛出运行时异常,不用加break,线程也由于异常而终止了。