Java线程的基本使用
1.1 线程的创建
1.1.1 创建方式1—继承Thread类
我们可以通过以下三个步骤通过继承Thread类的方式创建分线程
-
Step1:创建类继承Thread类并重写
run
方法class MyThread extends Thread { @Override public void run() { System.out.println("我是通过继承Thread类创建的分线程"); } }
-
Step2:创建MyThread类的实例对象
// 1. 创建线程实例 Thread t = new MyThread();
-
Step3:调用start方法创建出分线程
// 2. 调用start方法创建线程并运行 t.start();
-
执行结果:
1.1.2 创建方式2—实现Runnable接口
第二种创建线程的方式就是实现Runnable接口并重写run方法,可以通过以下步骤实现
-
Step1:创建Runnable接口实现类,并重写run方法
class MyThread implements Runnable { @Override public void run() { System.out.println("我是通过实现Runnable接口创建的分线程"); } }
-
Step2:创建Thread类实例,并传入Runnable接口实现类对象
// 1. Runnable实现类对象 Runnable r = new MyThread(); // 2. 创建出Thread类实例 Thread t = new Thread(r);
-
Step3:调用start方法创建出分线程
// 3. 调用start方法 t.start();
1.1.3 创建方式3—继承Thread类(匿名内部类)
我们可以使用继承Thread类的方式创建线程,但是使用匿名内部类更加简便。
下面是实现步骤:
-
Step1:通过匿名内部类的方式创建Thread实例
// 1. 创建Thread实例 Thread t = new Thread(){ @Override public void run() { System.out.println("我是通过实现继承Thread类(匿名内部类)创建的分线程"); } };
-
Step2:通过Thread实例调用start方法创建分线程
// 2. 调用start方法 t.start();
1.1.4 创建方式4—实现Runnable接口(匿名内部类)
我们可以使用实现Runnable接口的方式创建线程,但是使用匿名内部类更加简便。
下面是实现步骤:
-
Step1:创建Thread实例,通过匿名内部类的方式传参
// 1. 创建Thread实例(匿名内部类) Thread t = new Thread(new Runnable() { @Override public void run() { System.out.println("我是通过实现Runnable接口(匿名内部类)创建的分线程"); } });
-
Step2:调用start方法,创建分线程
// 2. 调用start方法 t.start();
1.1.5 创建方式5—实现Runnable接口(lambda表达式)
上述四种方式我们并不常用,使用lambda表达式可以更加简单高效
实现步骤如下:
-
创建Thread类实例,并传入lambda匿名对象
// 1. 创建Thread实例 Thread t = new Thread(() -> { System.out.println("我是通过lambda表达式创建的分线程"); });
-
调用实例的start方法创建分线程
// 2. 调用start方法 t.start();
1.2 Thread类的常用方法
1.2.1 Thread类的常用构造方法
方法 | 说明 |
---|---|
Thread() | 创建线程对象 |
Thread(String name) | 创建线程对象并命名 |
Thread(Runnable target) | 使用Runnable实现类对象创建线程对象 |
Thread(Runnable target, String name) | 使用Runnable实现类对象创建线程对象,并命名 |
Thread t1 = new Thread();
Thread t2 = new Thread("线程2");
Thread t3 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我的名字是:" + Thread.currentThread().getName());
}
});
Thread t4 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("我的名字是:" + Thread.currentThread().getName());
}
}, "线程4");
t1.start();
t2.start();
t3.start();
t4.start();
1.2.2 Thread类的常用属性和获取方式
属性 | 说明 |
---|---|
id | 区分线程的唯一标识符 |
name | 线程名称 |
state | 线程状态 |
priority | 线程优先级 |
isAlive() | 判断线程是否存活 |
isDaemon() | 判断线程是否是后台进程 |
isInterrupted() | 判断线程是否被中断 |
Thread t = new Thread(() -> {
System.out.println("我的名字是:" + Thread.currentThread().getName());
System.out.println("我的线程ID是:" + Thread.currentThread().getId());
System.out.println("我的线程优先级是:" + Thread.currentThread().getPriority());
System.out.println("我是否是后台进程:" + Thread.currentThread().isDaemon());
System.out.println("我是否被中断:" + Thread.currentThread().isInterrupted());
});
System.out.println("我现在的线程状态是:" + t.getState());
System.out.println("线程存活状态:" + t.isAlive());
t.start();
System.out.println("我现在的线程状态是:" + t.getState());
System.out.println("线程存活状态:" + t.isAlive());
1.3 线程中断
线程中断不同于操作系统/硬件设备中的中断,线程中断指的是通过一些标志位让正在工作状态的线程停止运行(即结束run方法的执行),线程中断我们可以采用如下两种方式实现:
- 通过共享的标记进行沟通
- 通过线程方法
interrupt()
进行通知
1.3.1 通过共享标记变量实现线程中断
public class ThreadDemo10 {
private static boolean flag;
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();
Thread.sleep(3000);
flag = true;
}
}
1.3.2 使用interrupt方法实现线程中断
我们可以通过interrupt()
和isInterrupted()
方法结合代替标志位的功能
public class ThreadDemo11 {
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(3000);
t.interrupt(); // 进行中断
}
}
注意下图所示的运行结果:我们惊奇的发现当线程处于Sleep阻塞时,被中断那么会抛出InterruptedException
异常,并且重置了中断标志,总结如下:
- 让线程因为调用了
sleep()
、wait()
、join()
等方法而阻塞被中断时,就会抛出InterruptedException
异常,并且重置中断标记,并且是否结束当前线程取决于catch块中的代码实现,可以继续执行线程也可以结束线程。 - 只是内部中断标记被设置,thread可以通过
- Thread.interrupted()判断当前线程的中断标记是否被设置,清除中断标志
- Thread.currentThread().isInterrupted()方法来判断线程的中断变量是否被设置,不清除中断标记
1.3.3 interrupted()方法验证
使用interrupted()方法验证中断变量是否被清除
public class ThreadDemo12 {
public static void main(String[] args) {
Thread t = new Thread(() -> {
for (int i = 0; i < 10; i++) {
// System.out.println("当前中断标记变量:" + Thread.interrupted());
System.out.println("当前中断标记变量:" + Thread.currentThread().isInterrupted());
}
});
t.start();
t.interrupt();
}
}
1.4 线程等待
有时候,我们需要等待一个线程执行完毕任务才可以继续执行当前线程的任务,例如说有一对情侣,其中女方想要买一串项链,但是需要1000元,只有丈夫赚够了1000元妻子才可以进行购买。此时妻子线程必须等待丈夫线程赚够1000元才可以继续执行,于是便引入了join
方法
public class ThreadDemo13 {
private static int deposit; // 夫妻共同财产
public static void main(String[] args) throws InterruptedException {
Thread husband = new Thread(() -> {
while (deposit < 1000) {
deposit += 100;
System.out.println(Thread.currentThread().getName() + "正在努力赚钱...当前余额为:" + deposit);
}
}, "丈夫");
husband.start();
husband.join();
System.out.println("当前余额为:" + deposit + ",妻子可以购买项链!");
}
}
其中,husband.join()
方法的作用就是让当前线程处于阻塞状态,等待husband线程执行结束后继续执行当前线程的任务
注意:一般在开发过程中很少使用不带参数的join()
方法,因为这是无休止的等待别的线程的执行,下面是join()
的一些重载方法
方法 | 说明 |
---|---|
public void join() | 等待线程结束 |
public void join(long millis) | 等待线程结束,最多等millis毫秒 |
public void join(long millis, int nanos) | 同理,但是精度更高 |
1.5 线程休眠
线程休眠其实就是sleep()
方法,在我们之前的案例中应该都已经使用过了,下面是一个简单示例:
public class ThreadDemo14 {
public static void main(String[] args) throws InterruptedException {
long startTime = System.currentTimeMillis();
Thread.sleep(1000);
long endTime = System.currentTimeMillis();
System.out.println("执行时间为:" + (endTime - startTime) + "ms");
}
}
方法 | 说明 |
---|---|
public static void sleep(long millis) throws InterruptedException | 休眠当前线程millis毫秒 |
public static void sleep(long millis, int nanos) throws InterruptedException | 更高精度的休眠 |
1.6 获取线程实例
这个方法我们也相当的熟悉
方法 | 说明 |
---|---|
public static Thread currentThread() | 返回当前线程引用对象 |
public class ThreadDemo15 {
public static void main(String[] args) {
Thread t = Thread.currentThread();
System.out.println("线程名称:" + t.getName());
}
}