多线程是Java中的常用操作,而要实现多线程,则需要借助于Thread类。
多线程的基本用法包括创建、终断、等待、休眠等。
线程的创建
创建一个新线程通常有两种方法:创建类并继承Thread、实现Runnable接口。
继承Thread类只需要重写run方法即可:
class myThread extends Thread {
@Override
public void run() {
System.out.println("这是一个新线程");
}
}
通过实例化对象并调用start方法启动新线程:
myThread t = new myThread();
t.start();
实现Runnable接口类似:
class MyRunnable implements Runnable {
@Override
public void run() {
//这里写新线程的执行操作
}
}
这时启动新线程的操作有所不同:
MyRunnable runnable = new MyRunnable();
Thread t = new Thread(runnable);
t.start();
为了使代码更加简洁可以使用匿名内部类或者lambda表达式(这里介绍lambda的写法):
lambda表达式的写法为:(参数1, 参数2, …) -> {//这里写具体实现的语句},即:
Thread t = new Thread(() -> {//run方法参数为空,则只写一个()即可
//这里写run方法要执行的操作
});
线程的休眠
线程的休眠是指让当前线程暂停执行一段时间,让出CPU的时间片,以便其他线程有机会执行。可以通过Thread.sleep休眠当前线程。但是,调用sleep可能出现受查异常,因此需要处理该异常:
Thread t = new Thread(() -> {
while (true) {
System.out.println("hello t");
try {
Thread.sleep(1000);//1000代表休眠1000ms,实际休眠时间是>=1000ms的
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
线程的中断
线程的中断一般有两种:通过设置自定义标识符中断、调用interrupt方法。
设置自定义标识符:
当我们设置一个普通的标识符时,如果使用匿名内部类或者lambda表达式会出现某些问题:
即匿名内部类中只能接收final或者说"实际 final"——即不变的量。
要解决这个问题可以考虑使用成员变量:
public class ThreadDemo {
public static boolean flag = false;
public static void main(String[] args) {
Thread t = new Thread(() -> {
while (!flag) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
flag = true;
}
}
调用interrupt()方法
通过Thread.currentThread.isInterrupted()获得标志位(Thread.currentThread获取当前线程,也就是t线程),再通过interrupt更改标志位使其不满足循环条件:
Thread t = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
System.out.println("hello t");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
t.interrupt();//设置标志位为true
不过这样并不能使程序停下:
这是由于sleep的缘故:
如果处于休眠态的线程被强行中断,则sleep会在抛出异常的同时清除中断状态。也就是说主线程中的t.interrupt()设置的一次标志中断休眠后又被清除了,这时就又满足循环条件。而主线程只设置了一次标志位,那么t线程就会继续不断地执行下去。
因此需要我们在打印异常信息后及时的break出去:
线程的等待
线程的等待是指线程在等待某个条件满足时进入休眠状态,等待被唤醒。
join方法可以使当前线程进入阻塞状态,即CPU停止调度当前线程,转而去调度其他的线程,等到其他的线程结束后再执行当前线程。例如:
Thread t = new Thread(() -> {
for (int i = 0; i < 3; i++) {
System.out.println("执行" + (i + 1) + "次");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
t.start();
//join方法中可以设置参数,参数为线程等待的最大时长
t.join();//只有t线程执行完毕,才能继续执行主线程
System.out.println("执行完毕!");
线程的等待除了通过join方法外,还可以通过加锁操作,即sychronized()方法(实际上是调用了Object.wait方法)等。