当前位置: 首页 > news >正文

Java线程:多任务进行

在现代软件开发中,多线程编程是提升程序性能、优化资源利用的关键技术之一。Java作为广泛应用的编程语言,为开发者提供了丰富且强大的线程工具。本文将深入探讨Java中线程的概念,并详细介绍Java提供的各类线程工具,助力读者在实际开发中高效利用多线程技术。

一、线程的概念

定义

线程是进程中的一个执行单元,它包含了程序执行中所需的寄存器、程序计数器和栈等信息。线程是 CPU 调度和分派的基本单位。一个进程可以包含多个线程,这些线程可以并发执行。例如,一个音乐播放软件进程,它可以有一个负责播放音乐的线程,一个负责更新播放列表显示的线程,它们可以同时运行,提高程序的执行效率。

线程与进程的区别

  • 资源拥有 :进程是独立的资源拥有者,它拥有自己的内存空间、文件描述符等资源。而线程不拥有系统资源,同一进程中的多个线程共享该进程的资源,包括内存、文件等。这意味着线程之间的通信相对简单,因为它们可以直接访问共享内存中的数据。
  • 系统开销 :创建和销毁进程的开销比较大,因为涉及到系统资源的分配和回收。而创建和销毁线程的开销相对较小,这使得线程在任务切换等场景中更加高效。
  • 独立性 :进程具有独立性,一个进程的崩溃一般不会影响其他进程(除非有特殊的共享内存等资源导致的连锁反应)。线程则不同,同一进程中的线程如果出现错误,可能会导致整个进程崩溃,因为它们共享资源。

线程的状态

  • 新建(New) :线程对象被创建后,就处于新建状态。此时它只是被分配了内存空间,还没有开始执行。例如,Thread thread = new Thread();,此时线程处于新建状态。
  • 就绪(Runnable) :当线程启动(调用 start() 方法)后,线程就处于就绪状态。这意味着线程已经具备了运行的条件,等待 CPU 时间片来执行。不过,它还没有真正开始运行,只是在操作系统线程调度器的可运行队列中等待。
  • 运行(Running) :线程获取到 CPU 时间片,开始执行线程的 run() 方法中的代码时,就处于运行状态。这是线程的主要活动状态。
  • 阻塞(Blocked) :线程在等待某个事件完成时会进入阻塞状态。例如,当线程尝试获取一个已经被其他线程锁定的对象锁时,就会进入阻塞状态,直到这个锁被释放。或者线程在等待 I/O 操作完成时也会被阻塞。
  • 等待(Waiting) :线程进入等待状态是因为调用了一些特定的方法,如 Object 类的 wait() 方法。线程在等待另一个线程的通知(通过 notify() 或 notifyAll() 方法),才会从等待状态恢复到就绪状态。
  • 定时等待(Timed Waiting) :这是等待状态的一种变体。线程在等待一个指定时间的事件,比如调用 Thread.sleep() 方法或者对象的 wait(long timeout) 方法。在线程等待的时间到了之后,它会从定时等待状态回到就绪状态。
  • 终止(Terminated) :线程的 run() 方法执行完毕,或者因为某种原因(如抛出未捕获的异常)而结束运行,线程就处于终止状态。此时线程不能再次被启动。

二、Java 中与线程相关的工具

Thread 类

这是 Java 中最基础的线程操作类。通过继承 Thread 类或者实现 Runnable 接口来创建线程。

  • 继承 Thread 类示例
public class MyThread extends Thread {@Overridepublic void run() {// 线程执行的代码for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "运行,i=" + i);}}
}
public class ThreadTest {public static void main(String[] args) {MyThread thread = new MyThread();thread.start(); // 启动线程}
}

在上面的例子中,我们创建了一个继承自 Thread 的 MyThread 类,并重写了 run() 方法。通过调用 start() 方法来启动线程,这会使得系统调度线程去执行 run() 方法中的代码。

  • 实现 Runnable 接口示例
public class MyRunnable implements Runnable {@Overridepublic void run() {// 线程执行的代码for (int i = 0; i < 5; i++) {System.out.println(Thread.currentThread().getName() + "运行,i=" + i);}}
}
public class RunnableTest {public static void main(String[] args) {MyRunnable runnable = new MyRunnable();Thread thread = new Thread(runnable);thread.start();}
}

实现 Runnable 接口的方式更加灵活,因为它允许一个类同时继承其他类并实现 Runnable 接口,用于定义线程任务。

Runnable 和 Callable 接口

  • Runnable 接口 :如前面所述,它定义了一个 run() 方法,没有返回值,也没有抛出异常的声明。适合用于没有返回结果的线程任务。
  • Callable 接口 :Callable 接口是在 Java 1.5 之后引入的,它定义了一个 call() 方法。call() 方法可以有返回值,并且可以抛出受检异常。这使得它可以用于有返回结果的线程任务。例如:
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;public class MyCallable implements Callable<Integer> {@Overridepublic Integer call() throws Exception {int sum = 0;for (int i = 0; i <= 100; i++) {sum += i;}return sum; // 返回计算结果}
}
public class CallableTest {public static void main(String[] args) {MyCallable callable = new MyCallable();FutureTask<Integer> futureTask = new FutureTask<>(callable);Thread thread = new Thread(futureTask);thread.start();try {Integer result = futureTask.get(); // 获取线程任务的返回结果System.out.println("结果:" + result);} catch (Exception e) {e.printStackTrace();}}
}

在这个例子中,通过 FutureTask 来包装 Callable 对象,这样可以获取线程任务执行后的返回结果。

Executor 框架

  • Executor 接口 :它是 Java 中的一个执行器接口,用于将线程的创建和任务的执行分离开来。它提供了一种更高级的线程管理方式。
  • ThreadPoolExecutor 类 :这是 Executor 框架中的线程池实现类。线程池可以有效地控制线程的数量和生命周期,提高线程的复用性,减少线程频繁创建和销毁的开销。例如:
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class ThreadPoolTest {public static void main(String[] args) {ExecutorService executorService = Executors.newFixedThreadPool(3); // 创建一个固定大小为 3 的线程池for (int i = 0; i < 10; i++) {executorService.execute(() -> {System.out.println(Thread.currentThread().getName() + "正在执行任务");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});}executorService.shutdown(); // 关闭线程池}
}

在这个例子中,我们通过 Executors 创建了一个固定大小为 3 的线程池。然后通过 execute() 方法提交任务给线程池执行。线程池会根据设置的线程数量来复用线程执行任务,当不再提交任务时,通过 shutdown() 方法可以优雅地关闭线程池。

  • ScheduledExecutorService 接口和 ScheduledThreadPoolExecutor 类 :ScheduledExecutorService 是 ExecutorService 的子接口,主要用于任务的定时和周期性调度。ScheduledThreadPoolExecutor 是它的实现类。例如,可以用于定时执行任务或者按照一定周期执行任务:
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;public class ScheduledTest {public static void main(String[] args) {ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(2);// 安排任务在延迟 2 秒后执行一次scheduledExecutorService.schedule(() -> {System.out.println("延迟任务执行");}, 2, TimeUnit.SECONDS);// 安排任务在初始延迟 1 秒后,每隔 3 秒执行一次scheduledExecutorService.scheduleAtFixedRate(() -> {System.out.println("周期性任务执行");}, 1, 3, TimeUnit.SECONDS);// 在合适的时候可以调用 scheduledExecutorService.shutdown() 关闭}
}

线程同步工具类

  • Object 类的 wait()、notify() 和 notifyAll() 方法 :这些方法用于线程之间的通信。wait() 方法使线程进入等待状态,释放对象锁;notify() 方法唤醒一个等待在该对象上的线程;notifyAll() 方法唤醒所有等待在该对象上的线程。它们通常用于实现线程间的协作,例如生产者 - 消费者模式。
  • Lock 接口和 ReentrantLock 类 :Lock 是比 synchronized 更加灵活的锁机制。ReentrantLock 是 Lock 接口的一个实现类,它提供了和 synchronized 相似的功能,但是具有更高的灵活性。例如,可以尝试获取锁(tryLock() 方法),并且可以设置公平锁等。与 synchronized 相比,它需要手动获取和释放锁,代码示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class LockTest {private final Lock lock = new ReentrantLock();public void methodA() {lock.lock(); // 获取锁try {// 操作共享资源的代码System.out.println(Thread.currentThread().getName() + "执行 methodA");} finally {lock.unlock(); // 释放锁}}public void methodB() {lock.lock();try {System.out.println(Thread.currentThread().getName() + "执行 methodB");} finally {lock.unlock();}}
}

在这个例子中,通过 ReentrantLock 来实现对共享资源的访问控制,保证同一时间只有一个线程可以执行 methodA 或 methodB 中的代码。

  • CountDownLatch 类 :它是一个同步辅助类,允许一个或多个线程一直等待,直到其他线程完成一系列操作。例如,在启动多个线程后,等待它们全部完成再进行后续操作:
import java.util.concurrent.CountDownLatch;public class CountDownLatchTest {public static void main(String[] args) throws InterruptedException {final CountDownLatch countDownLatch = new CountDownLatch(3); // 初始计数为 3for (int i = 0; i < 3; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "开始执行");try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}countDownLatch.countDown(); // 执行完任务后计数减 1System.out.println(Thread.currentThread().getName() + "执行结束");}).start();}countDownLatch.await(); // 主线程等待计数为 0System.out.println("所有线程执行完毕,开始后续操作");}
}
  • CyclicBarrier 类 :它也是一个同步辅助类,允许一组线程相互等待,直到所有线程都到达一个公共屏障点。它可以在某些批处理场景下使用,例如,多个线程处理不同的数据分片,然后在所有线程处理完后进行汇总:
import java.util.concurrent.CyclicBarrier;public class CyclicBarrierTest {public static void main(String[] args) {final CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> {System.out.println("所有线程都到达屏障,开始进行汇总操作");}); // 设置屏障的线程数量为 3,并且定义了当所有线程到达屏障后的任务for (int i = 0; i < 3; i++) {new Thread(() -> {System.out.println(Thread.currentThread().getName() + "开始处理数据");try {Thread.sleep(1000);cyclicBarrier.await(); // 线程到达屏障点} catch (Exception e) {e.printStackTrace();}System.out.println(Thread.currentThread().getName() + "继续后续操作");}).start();}}
}
  • Semaphore 类 :Semaphore 是一个计数信号量,用于控制同时访问特定资源的线程数量。它可以通过 acquire() 方法获取许可,release() 方法释放许可。例如,限制对某个资源的最大并发访问数:
import java.util.concurrent.Semaphore;public class SemaphoreTest {private final Semaphore semaphore = new Semaphore(3); // 设置许可数量为 3public void accessResource() {try {semaphore.acquire(); // 获取许可System.out.println(Thread.currentThread().getName() + "获取到许可,正在访问资源");Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();} finally {semaphore.release(); // 释放许可System.out.println(Thread.currentThread().getName() + "释放许可");}}public static void main(String[] args) {SemaphoreTest semaphoreTest = new SemaphoreTest();for (int i = 0; i < 6; i++) {new Thread(semaphoreTest::accessResource).start();}}
}

在 Java 的多线程世界里,这些线程工具为我们提供了强大的助力。从基础的线程概念到丰富多样的工具类,它们各自发挥着独特的作用,帮助我们构建出高效、可靠的并发程序。只有深入理解并灵活运用这些工具,我们才能在 Java 开发中充分发挥多线程的优势,让程序真正地 “多任务” 起来,为用户提供更流畅、更高效的软件体验。

相关文章:

  • 不同类型桥梁的无人机检测内容及技术难度
  • 无人机遥控器光纤通信模块技术要点!
  • 12.vue整合springboot首页显示数据库表-实现按钮:【添加修改删除查询】
  • SpringBoot-1-入门概念介绍和第一个Spring Boot项目
  • @RequestParam 和 @RequestBody、HttpServletrequest 与HttpServletResponse
  • 计算机网络-HTTP与HTTPS
  • unigui 监听控件的js事件
  • Agent的工作原理是什么?一文详解Agent的工作原理
  • MySQL之函数
  • 高速光耦在通信行业的应用(六) | 5Mbps通信光耦的应用
  • 【图像大模型】FLUX.1-dev:深度解析与实战指南
  • 《 二级指针:解锁指针的进阶魔法》
  • 新书速览|鸿蒙HarmonyOS NEXT开发之路 卷2:从入门到应用篇
  • mes系统实施方案,mes解决方案(Word)
  • 数据中心 智慧机房解决方案
  • CS50x 01 c
  • 什么是RDMA?
  • 2025.05.19【Barplot】柱状图的多样性绘制
  • 大语言模型(LLM)本身是无状态的,怎么固化记忆
  • PyLops 使用与介绍
  • 英伟达推出新技术加速AI芯片连接,期望构建互联互通生态
  • 媒体:多家国有大行存款利率即将迎来新一轮下调
  • 上海中心城区首条“定制化低空观光航线”启航,可提前一天提需求
  • 肖钢:一季度证券业金融科技投资强度在金融各子行业中居首
  • 北斗系统全面进入11个国际组织的标准体系
  • 马上评|科学红毯,让科学家成为“最亮的星”