
00:00:00
外卖卷:非常划算 扫码领劵 省点小钱钱
Java 语言内置了多线程支持。当 Java 程序启动的时候,实际上是启动了一个 JVM 进程,然后,JVM 启动主线程来执行
main()方法。在main()方法中,我们又可以启动其他线程。
程序:程序是一组为完成特定任务而用某种语言编写的指令集合。
进程:进程是程序的一次执行过程,或是正在运行的一个程序。
线程:线程是由进程创建的一个执行单元。可以理解为进程中的一个“子任务”,一个进程可以包含多个线程。
其他相关概念
在 Java 程序中,一个线程对象只能调用一次 start() 方法启动新线程,并在新线程中执行 run() 方法。一旦 run() 方法执行完毕,线程就结束了。因此,Java 线程的生命周期包括以下几种状态:
NEW(新建)
start() 方法,此时线程处于新建状态。RUNNABLE(可运行)
start() 方法,进入可运行状态。TIMED_WAITING(超时等待)
Thread.sleep()、join(timeout) 或 wait(timeout) 方法后,线程会处于此状态。WAITING(等待)
Object.wait() 后线程会进入此状态,直到被其他线程通过 notify() 或 notifyAll() 唤醒。BLOCKED(阻塞)
TERMINATED(终止)
run() 方法执行完毕或因异常退出后,线程进入终止状态,表示生命周期结束。有些书中说一共有 7 个状态,实际上就是将 Runnable 状态中 Ready 和 Running 分开了。到底是否运行还是取决于内核态的调度情况。

继承 Thread 类并重写其 run() 方法来创建线程对象,然后调用 start() 方法启动线程。
Thread,代码较为简单。public class Main {
public static void main(String[] args) {
Thread t = new MyThread();
t.start(); // 启动新线程
}
}
class MyThread extends Thread {
@Override
public void run() {
System.out.println("start new thread!");
}
}实现 Runnable 接口并重写其 run() 方法,然后将该实现类的实例传入 Thread 构造器,再调用 start() 方法启动线程。
Runnable 实现类可以被多个线程使用。Runnable,设计更加灵活。public class Main {
public static void main(String[] args) {
Thread t = new Thread(new MyRunnable());
t.start(); // 启动新线程
}
}
class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("start new thread!");
}
}public class Main {
public static void main(String[] args) {
Thread t = new Thread(() -> {
System.out.println("start new thread!");
});
t.start(); // 启动新线程
}
}两者的区别
Thread 类本身就实现了 Runnable 接口,因此通过继承 Thread 或实现 Runnable 创建线程在本质上没有区别。线程常用方法
| 方法 | 描述 |
|---|---|
setName(String name) | 设置线程名称,使线程的名称与参数 name 相同 |
getName() | 返回线程的名称 |
start() | 启动线程,调用 Java 虚拟机底层的 start0 方法来执行线程 |
run() | 线程执行的具体任务,通常在 run 方法中编写线程的工作逻辑 |
setPriority(int priority) | 更改线程的优先级,优先级范围为 1(最低)到 10(最高),默认值为 5 |
getPriority() | 获取线程的优先级 |
sleep(long millis) | 让当前线程休眠指定的毫秒数(暂停执行),可以用来模拟任务延迟或降低线程频率 |
interrupt() | 中断线程,通知线程停止当前操作或退出,需配合线程内的中断状态检查使用 |
yield() | 线程的礼让,当前线程主动让出 CPU,允许其他线程执行。礼让的时间不确定,当前线程可能并不会立即让出 CPU |
join() | 线程的插队,当前线程会等待调用该方法的线程执行完毕后,再继续执行。插队线程的任务必须先执行完 |
线程正常终止:run() 方法执行到 return 语句返回。
线程意外终止:run() 方法因为未捕获的异常导致线程终止。
通知线程终止:通过设置标志变量来控制线程的运行状态。
public class ThreadDemo {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start();
try {
Thread.sleep(2000); // 主线程等待 2 秒
} catch (InterruptedException e) {
e.printStackTrace();
}
// 通知子线程退出
thread.stopThread();
System.out.println("通知子线程停止。");
}
}class MyThread extends Thread {
// 定义标志变量,用于控制线程退出
private boolean running = true;
@Override
public void run() {
while (running) {
System.out.println("线程正在运行...");
try {
Thread.sleep(500); // 模拟线程工作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("线程已停止。");
}
// 提供方法来修改标志变量,从而通知线程退出
public void stopThread() {
running = false;
}
}线程正在运行...
线程正在运行...
线程正在运行...
线程正在运行...
线程正在运行...
通知子线程停止。
线程已停止。
进程已结束,退出代码为 0线程中断是 Java 中的一种机制,用来通知一个线程应该停止当前的工作或者改变状态。
注意:中断并不会强制线程终止,而是通过设置“中断标志”(interrupt flag)来给线程传递一个信号。
线程可以根据这个信号自行决定如何响应,比如退出、重新尝试任务或清理资源。
中断标志位:
interrupt() 方法会将目标线程的中断标志设置为 true。isInterrupted() 检查自己的中断状态。响应中断的两种方式:
检查中断标志:线程定期调用 isInterrupted(),发现中断标志为 true 时自行退出或执行其他逻辑。
public class InterruptFlagExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) { // 检查中断标志
System.out.println("线程运行中...");
try {
Thread.sleep(1000); // 模拟工作
} catch (InterruptedException e) {
System.out.println("线程在休眠时被中断!");
Thread.currentThread().interrupt(); // 重新设置中断标志
}
}
System.out.println("线程已中断,退出运行。");
});
thread.start();
try {
Thread.sleep(3000); // 主线程休眠3秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 主线程中断子线程
}
}线程运行中...
线程运行中...
线程运行中...
线程在休眠时被中断!
线程已中断,退出运行。
进程已结束,退出代码为 0捕获 InterruptedException:如果线程在调用阻塞方法时被中断,会抛出 InterruptedException。
public class InterruptedExceptionExample {
public static void main(String[] args) {
Thread thread = new Thread(() -> {
try {
System.out.println("线程开始运行...");
Thread.sleep(5000); // 模拟阻塞操作
System.out.println("线程完成工作。");
} catch (InterruptedException e) {
System.out.println("线程被中断,停止工作。");
}
});
thread.start();
try {
Thread.sleep(2000); // 主线程等待2秒
} catch (InterruptedException e) {
e.printStackTrace();
}
thread.interrupt(); // 主线程中断子线程
}
}线程开始运行...
线程被中断,停止工作。
进程已结束,退出代码为 0Thread.setDaemon(true) 将线程标记为守护线程。一旦标记后,程序结束时守护线程会随之自动终止。public class DaemonThreadDemo {
public static void main(String[] args) {
// 创建一个普通线程
Thread regularThread = new Thread(new RegularTask());
regularThread.start();
// 创建一个守护线程
Thread daemonThread = new Thread(new DaemonTask());
daemonThread.setDaemon(true); // 设置为守护线程
daemonThread.start();
// 主线程的任务
try {
Thread.sleep(2000); // 主线程睡眠2秒,等待其他线程执行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("主线程执行完毕,程序结束");
}
}class DaemonTask implements Runnable {
@Override
public void run() {
while (true) {
try {
System.out.println("守护线程在执行...");
Thread.sleep(1000); // 模拟守护线程的工作
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}class RegularTask implements Runnable {
@Override
public void run() {
try {
System.out.println("普通线程开始执行...");
Thread.sleep(5000); // 模拟一些需要较长时间的操作
System.out.println("普通线程执行完毕");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}普通线程开始执行...
守护线程在执行...
守护线程在执行...
主线程执行完毕,程序结束
守护线程在执行...
守护线程在执行...
守护线程在执行...
普通线程执行完毕
进程已结束,退出代码为 0在多线程编程中,为了保护敏感数据的完整性,必须使用线程同步技术。即当一个线程正在操作某块内存时,其他线程必须等待,直到操作完成后才能访问该内存。这确保了同一时刻最多只有一个线程访问该数据。
同步代码块
synchronized (this) { // 锁住 this 或其他锁对象
// 需要被同步代码;
}同步方法
public synchronized void m(String name) { // 锁住 this
// 需要被同步的代码
}基本介绍
synchronized 来与对象的互斥锁联系。当某个对象用 synchronized 修饰时,表明该对象在任一时刻只能由一个线程访问。this,也可以是其他对象(要求是同一个对象)。注意事项和细节
static 修饰,默认锁对象为 this。static 修饰,默认锁对象为当前类的 Class 对象。实现步骤
下面操作会释放锁
break、return。Error 或 Exception,导致异常结束。wait() 方法,当前线程暂停,并释放锁。下面操作不会释放锁
Thread.sleep()、Thread.yield() 方法暂停当前线程的执行,不会释放锁。suspend() 方法将该线程挂起,该线程不会释放锁。提示:应尽量避免使用 suspend() 和 resume() 来控制线程,方法不再推荐使用。死锁就是多个线程都占用了对方的锁资源,但不肯相让,导致了死锁,在编程中一定要避免死锁的发生。
public class DeadLockDemo {
public static void main(String[] args) {
// 创建两个线程模拟死锁
DeadLockThread threadA = new DeadLockThread(true);
threadA.setName("线程A");
DeadLockThread threadB = new DeadLockThread(false);
threadB.setName("线程B");
threadA.start();
threadB.start();
}
}// 模拟死锁的线程类
class DeadLockThread extends Thread {
// 定义两个共享的锁对象
private static final Object lock1 = new Object();
private static final Object lock2 = new Object();
private final boolean flag; // 决定线程的锁顺序
/**
* 构造函数
* @param flag 如果为 true,先锁住 lock1,再尝试锁 lock2;反之亦然。
*/
public DeadLockThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
synchronized (lock1) { // 锁住 lock1
System.out.println(Thread.currentThread().getName() + " 锁住 lock1,尝试获取 lock2...");
synchronized (lock2) { // 尝试锁住 lock2
System.out.println(Thread.currentThread().getName() + " 成功锁住 lock2!");
}
}
} else {
synchronized (lock2) { // 锁住 lock2
System.out.println(Thread.currentThread().getName() + " 锁住 lock2,尝试获取 lock1...");
synchronized (lock1) { // 尝试锁住 lock1
System.out.println(Thread.currentThread().getName() + " 成功锁住 lock1!");
}
}
}
}
}
评论