您的位置:首页 > 教育 > 培训 > 深圳网站制作的公司_嘉兴做网站软件_襄阳百度开户_谷歌seo外链

深圳网站制作的公司_嘉兴做网站软件_襄阳百度开户_谷歌seo外链

2025/4/16 1:35:01 来源:https://blog.csdn.net/Chenchen0905_/article/details/147100542  浏览:    关键词:深圳网站制作的公司_嘉兴做网站软件_襄阳百度开户_谷歌seo外链
深圳网站制作的公司_嘉兴做网站软件_襄阳百度开户_谷歌seo外链

线程基础知识全面解析

  • 线程池(一)线程基础知识全面解析
    • 1.1 线程和进程的区别?
      • 定义
      • 资源占用
      • 独立性
      • 通信与同步
    • 1.2 并行和并发有什么区别?
      • 并行(Parallelism)
      • 并发(Concurrency)
      • 总结对比
    • 1.3 创建线程的四种方式
      • 方式一:继承Thread类
      • 方式二:实现Runnable接口
      • 方式三:实现Callable接口(带返回值)
      • 方式四:使用线程池
    • 1.4 runnable和callable有什么区别
      • 方法定义
      • 异常处理
      • 返回值
      • 应用场景
    • 1.5 线程的 run()和 start()有什么区别?
      • run()方法
      • start()方法
    • 1.6 线程包括哪些状态,状态之间是如何变化的
      • 线程的状态(根据`java.lang.Thread.State`枚举)
      • 状态变化示例
    • 1.7 新建T1、T2、T3三个线程,如何保证它们按顺序执行?
    • 1.8 notify()和 notifyAll()有什么区别?
      • notify()
      • notifyAll()
      • 示例对比
    • 1.9 在Java中wait和sleep方法的不同?
      • 所属类
      • 是否释放锁
      • 用法与场景
      • 异常处理
    • 1.10 如何停止一个正在运行的线程?
      • 不推荐的方式:stop()方法
      • 推荐方式:使用标志位
      • 使用`interrupt()`方法

线程池(一)线程基础知识全面解析

1.1 线程和进程的区别?

定义

  • 进程:进程是程序在操作系统中的一次执行过程,是系统进行资源分配和调度的基本单位。每个进程都有独立的内存空间、系统资源(如文件描述符、进程间通信资源等)。例如,当我们打开一个浏览器应用,操作系统会为该浏览器创建一个进程,浏览器中的各个标签页、插件等可能在这个进程内或通过其他进程协作运行。
  • 线程:线程是进程中的一个执行单元,是程序执行的最小单位。线程共享所属进程的资源,如内存空间、文件描述符等。一个进程可以包含多个线程,这些线程并发执行,提高程序的执行效率。比如,在一个视频编辑软件进程中,可能有一个线程负责视频渲染,另一个线程负责用户界面的交互响应。

资源占用

  • 进程:进程拥有独立的地址空间,创建进程时,系统会为其分配较多的资源,包括内存、CPU时间片、文件描述符等。因此,进程的创建和销毁开销较大。
  • 线程:线程共享进程的地址空间,只需为其分配少量的资源(如栈空间、寄存器等),创建和销毁的开销相对较小。

独立性

  • 进程:不同进程之间相互独立,一个进程的崩溃通常不会影响其他进程(除非存在共享资源的特殊情况,如共享内存未正确管理)。
  • 线程:同一进程内的线程共享进程资源,一个线程的崩溃可能导致整个进程崩溃(例如,线程访问了非法内存地址,引发进程级别的错误)。

通信与同步

  • 进程:进程间通信(IPC)需要通过专门的机制,如管道、消息队列、共享内存、套接字等,实现相对复杂。
  • 线程:同一进程内的线程间通信更为方便,可直接通过共享变量进行数据交换,但需要注意同步问题,避免数据竞争(如使用synchronizedLock等机制保证线程安全)。

1.2 并行和并发有什么区别?

并行(Parallelism)

  • 定义:指在同一时刻,多个任务在不同的物理处理器核心上同时执行。例如,一个4核CPU可以同时处理4个不同的任务,每个任务在独立的核心上运行,这就是并行。
  • 特点:需要多核处理器支持,真正实现了任务的同时执行,能有效利用多核资源,提升系统整体性能。

并发(Concurrency)

  • 定义:指在一段时间内,多个任务交替执行,宏观上看起来像是同时进行。但在单处理器核心的情况下,同一时刻实际上只有一个任务在执行,通过快速切换(如时间片轮转),给人一种多个任务同时运行的错觉。例如,操作系统同时运行多个程序,每个程序的任务在时间片内轮流使用CPU。
  • 特点:更侧重于任务的管理和调度,不一定需要多核处理器,主要通过调度算法让多个任务在有限的资源(如单CPU)上交替执行,提高资源利用率和系统的响应速度。

总结对比

  • 并行是物理上的同时执行,依赖多核硬件;并发是逻辑上的同时“处理”,通过调度实现任务的交替执行。可以说,并行是并发的一种特殊情况,并发的概念更广泛。

1.3 创建线程的四种方式

方式一:继承Thread类

class MyThread extends Thread {@Overridepublic void run() {// 线程执行的代码System.out.println("继承Thread类创建的线程运行");}
}// 使用
MyThread thread = new MyThread();
thread.start();
  • 特点:代码简单直观,但Java单继承限制,若已继承其他类则无法采用。

方式二:实现Runnable接口

class MyRunnable implements Runnable {@Overridepublic void run() {System.out.println("实现Runnable接口创建的线程运行");}
}// 使用
Thread thread = new Thread(new MyRunnable());
thread.start();
  • 特点:避免单继承限制,适合多个线程共享同一资源的场景(如多个线程操作同一数据库连接)。

方式三:实现Callable接口(带返回值)

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;class MyCallable implements Callable<String> {@Overridepublic String call() throws Exception {return "实现Callable接口创建的线程返回值";}
}// 使用
MyCallable callable = new MyCallable();
FutureTask<String> futureTask = new FutureTask<>(callable);
Thread thread = new Thread(futureTask);
thread.start();
try {String result = futureTask.get(); // 获取返回值System.out.println(result);
} catch (InterruptedException | ExecutionException e) {e.printStackTrace();
}
  • 特点:能返回执行结果,可抛出异常,需配合FutureTask使用。

方式四:使用线程池

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;// 使用
ExecutorService executorService = Executors.newFixedThreadPool(5); // 创建固定大小线程池
executorService.submit(() -> {System.out.println("线程池中的线程运行");
});
executorService.shutdown(); // 关闭线程池
  • 特点:重用线程,减少创建销毁开销,控制线程数量,提高系统稳定性和性能。

1.4 runnable和callable有什么区别

方法定义

  • Runnable:只有一个run()方法,无返回值,void run()
  • Callable:有一个call()方法,可返回值,V call(),泛型V表示返回值类型。

异常处理

  • Runnable:不能抛出受检异常(checked exception),若方法内出现异常,需在内部处理。
  • Callable:可以抛出受检异常,调用者可通过FutureTaskget()方法获取异常信息(包装在ExecutionException中)。

返回值

  • Runnable:无返回值,主要用于执行无返回结果的任务。
  • Callable:有返回值,适用于需要返回执行结果的场景,如计算任务的结果返回。

应用场景

  • Runnable:适合简单的任务执行,如打印日志、无返回的业务逻辑处理。
  • Callable:适合需要返回结果的场景,如数据计算、远程调用后获取结果等。

1.5 线程的 run()和 start()有什么区别?

run()方法

  • 本质:是线程类(Thread)或实现Runnable接口类中的方法,定义了线程的执行逻辑。
  • 调用方式:若直接调用run()方法,它只是一个普通的方法调用,在当前线程(调用run()的线程)中执行,不会开启新线程。例如:
Thread thread = new Thread(() -> {System.out.println("run()方法直接调用");
});
thread.run(); // 不会开启新线程,在当前线程执行

start()方法

  • 本质:是Thread类的方法,用于启动一个新线程。
  • 调用方式:调用start()后,JVM会为线程分配资源,将线程状态从新建(NEW)转换为就绪(RUNNABLE),等待CPU调度执行run()方法。这是正确启动线程的方式,会真正开启新线程。例如:
Thread thread = new Thread(() -> {System.out.println("start()方法启动线程");
});
thread.start(); // 开启新线程

1.6 线程包括哪些状态,状态之间是如何变化的

线程的状态(根据java.lang.Thread.State枚举)

  • NEW(新建):线程已创建,但尚未调用start()方法,如Thread thread = new Thread(() -> {});
  • RUNNABLE(就绪/运行):包括就绪和运行两种子状态。就绪表示线程已准备好,等待CPU调度;运行表示线程正在执行。调用start()后,线程进入此状态。
  • BLOCKED(阻塞):线程等待锁(如synchronized同步块)时进入此状态,例如一个线程试图进入一个被其他线程占用的同步代码块。
  • WAITING(无限期等待):线程调用Object.wait()(无超时参数)、Thread.join()(无超时参数)或LockSupport.park()等方法后进入此状态,等待其他线程操作唤醒。
  • TIMED_WAITING(定时等待):线程调用Thread.sleep(long timeout)Object.wait(long timeout)Thread.join(long timeout)LockSupport.parkNanos(long nanos)等带超时参数的方法后进入此状态,在指定时间内等待。
  • TERMINATED(终止):线程执行完毕(run()方法结束)或因异常终止,如run()方法执行完成后,线程进入此状态。

状态变化示例

  1. NEW → RUNNABLE:调用thread.start(),线程从新建状态进入就绪(进而可能转为运行)状态。
  2. RUNNABLE → BLOCKED:线程尝试获取锁(如进入synchronized代码块),但锁被其他线程占用,进入阻塞状态。
  3. RUNNABLE → WAITING:线程调用object.wait()(无超时),释放锁并进入无限期等待状态,需其他线程调用object.notify()object.notifyAll()唤醒。
  4. RUNNABLE → TIMED_WAITING:线程调用Thread.sleep(1000),进入定时等待状态,1秒后自动回到就绪状态。
  5. BLOCKED → RUNNABLE:线程获取到锁(如占用锁的线程释放锁),从阻塞状态进入就绪状态,等待CPU调度。
  6. WAITING → RUNNABLE:线程被notify()notifyAll()唤醒,从等待状态进入就绪状态。
  7. TIMED_WAITING → RUNNABLE:定时等待时间到(如sleep时间结束),或被提前唤醒(notify等),进入就绪状态。
  8. RUNNABLE → TERMINATED:线程的run()方法执行完毕,或run()方法中抛出未处理的异常,线程进入终止状态。

1.7 新建T1、T2、T3三个线程,如何保证它们按顺序执行?

可以使用join()方法实现。join()方法会使调用线程(等待线程)等待被调用线程(如T1.join()表示当前线程等待T1线程执行完毕)执行完成后再继续执行。示例代码如下:

public class ThreadOrderDemo {public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println("T1执行");});Thread t2 = new Thread(() -> {try {t1.join(); // T2等待T1执行完System.out.println("T2执行");} catch (InterruptedException e) {e.printStackTrace();}});Thread t3 = new Thread(() -> {try {t2.join(); // T3等待T2执行完System.out.println("T3执行");} catch (InterruptedException e) {e.printStackTrace();}});t3.start();t2.start();t1.start();}
}

在上述代码中:

  • t3线程启动后,调用t2.join(),会等待t2线程执行。
  • t2线程启动后,调用t1.join(),会等待t1线程执行。
  • t1线程启动后执行打印,完成后t2继续执行,t2完成后t3继续执行,从而保证T1 → T2 → T3的顺序。

1.8 notify()和 notifyAll()有什么区别?

notify()

  • 作用:随机唤醒一个在该对象上等待的线程(处于WAITINGTIMED_WAITING状态)。例如,多个线程调用了object.wait()等待,object.notify()会唤醒其中一个。
  • 场景:适用于有多个等待线程,但只需要唤醒一个即可继续执行的场景,如资源池中有一个资源可用,唤醒一个等待线程获取资源。

notifyAll()

  • 作用:唤醒所有在该对象上等待的线程。这些线程会竞争锁(如进入synchronized代码块),获取锁的线程继续执行。
  • 场景:适用于需要唤醒所有等待线程的情况,如资源池被重置,所有等待资源的线程都需要重新竞争获取资源,避免部分线程因notify()未被唤醒而一直等待(可能导致死锁)。

示例对比

public class NotifyDemo {private static final Object LOCK = new Object();public static void main(String[] args) {for (int i = 0; i < 5; i++) {new Thread(() -> {synchronized (LOCK) {try {System.out.println(Thread.currentThread().getName() + "等待");LOCK.wait();System.out.println(Thread.currentThread().getName() + "被唤醒");} catch (InterruptedException e) {e.printStackTrace();}}}, "Thread-" + i).start();}try {Thread.sleep(1000); // 确保线程都进入等待} catch (InterruptedException e) {e.printStackTrace();}synchronized (LOCK) {// LOCK.notify(); // 随机唤醒一个LOCK.notifyAll(); // 唤醒所有}}
}

若使用notify(),可能只有一个线程被唤醒;使用notifyAll(),所有等待线程都会被唤醒去竞争锁。

1.9 在Java中wait和sleep方法的不同?

所属类

  • wait():是Object类的方法,所有对象都继承自Object,因此都可以调用wait()方法。
  • sleep():是Thread类的静态方法,通过Thread.sleep()调用。

是否释放锁

  • wait():调用wait()方法时,线程会释放持有的对象锁(如在synchronized代码块中),进入等待状态。其他线程可以获取该锁。
  • sleep():调用sleep()方法时,线程不会释放锁(如果持有锁),只是暂时停止执行,让CPU资源给其他线程,但锁仍然被持有。

用法与场景

  • wait():通常用于线程间的通信和协作,如一个线程等待某个条件满足(由其他线程通知)。必须在synchronized代码块或方法中使用。
synchronized (object) {while (条件不满足) {object.wait();}// 条件满足后执行
}
  • sleep():用于让线程暂停执行一段时间,不涉及线程间的通信。例如,定时执行任务,每隔一段时间执行一次操作。
try {Thread.sleep(1000); // 线程暂停1秒
} catch (InterruptedException e) {e.printStackTrace();
}

异常处理

  • wait():会抛出InterruptedException,需捕获处理。
  • sleep():同样会抛出InterruptedException,需捕获处理。

1.10 如何停止一个正在运行的线程?

不推荐的方式:stop()方法

  • stop()方法已被废弃,因为它会强制终止线程,可能导致线程持有的锁未释放、数据不一致等问题(如操作文件或数据库时突然停止,导致文件损坏或数据未正确提交)。

推荐方式:使用标志位

  • 在类中定义一个标志变量,线程内部根据标志决定是否继续执行。
public class StopThreadDemo {private static volatile boolean stopFlag = false; // volatile保证可见性public static void main(String[] args) {Thread thread = new Thread(() -> {while (!stopFlag) {// 线程执行的任务System.out.println("线程运行");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}System.out.println("线程停止");});thread.start();try {Thread.sleep(5000); // 5秒后停止} catch (InterruptedException e) {e.printStackTrace();}stopFlag = true; // 设置标志为true,线程检查后退出}
}
  • 上述代码中,stopFlagvolatile类型,保证多个线程间的可见性。当主线程设置stopFlag = true后,运行的线程检测到该标志,退出循环,安全停止。

使用interrupt()方法

  • 通过interrupt()方法设置线程的中断标志,线程内部检查中断标志并处理。
public class InterruptThreadDemo {public static void main(String[] args) {Thread thread = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {// 线程执行的任务System.out.println("线程运行");try {Thread.sleep(1000);} catch (InterruptedException e) {Thread.currentThread().interrupt(); // 重新设置中断标志System.out.println("捕获到中断,处理后继续检查标志");}}System.out.println("线程停止");});thread.start();try {Thread.sleep(5000); // 5秒后中断} catch (InterruptedException e) {e.printStackTrace();}thread.interrupt(); // 设置中断标志}
}
  • 在线程中,sleep()等方法会响应中断,抛出InterruptedException。处理完异常后,可再次检查中断标志,决定是否继续执行。若线程不涉及sleep()等可中断方法,也可直接通过isInterrupted()检查标志来停止。

通过合理使用标志位或interrupt()方法,能安全、优雅地停止线程,避免强制终止带来的问题。

以上就是线程基础知识的详细解析,涵盖了线程与进程的区别、并行并发、创建方式、重要方法对比、状态变化、线程协作与控制等核心内容,希望对深入理解线程机制有所帮助。

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com