【Java系列】深入解析Java多线程(上)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
云解析 DNS,旗舰版 1个月
简介: 【Java系列】深入解析Java多线程

1 基础介绍

什么是多线程

多线程

  • 指的是在一个进程中同时运行多个线程,每个线程都可以独立执行不同的任务或操作。
  • 与单线程相比,多线程可以提高程序的并发性和响应能力。

什么是进程

进程:

是指正在运行的程序的实例

每个进程都拥有自己的内存空间、代码、数据和文件等资源,可以独立运行、调度和管理。在操作系统中进程是系统资源分配的最小单位,是实现多任务的基础

在Java中,每个Java虚拟机(JVM)都是一个进程,可以同时运行多个Java程序,每个Java程序都是一个独立的进程


2 Java多线程

Java多线程是指在一个Java程序中同时执行多个线程,它可以提高程序的并发性和响应能力。Java中实现多线程的方式:

  1. 继承Thread类
  2. 实现Runnable接口
  3. Executor框架
  4. Callable
  5. Future
  6. 线程池

1 继承Thread类

继承Thread类是实现多线程的一种方式,只需要继承Thread类并重写run()方法即可。

public class ThreadDemo {
    public static void main(String[] args) {
        // 创建10个线程并启动
        for (int i = 0; i < 10; i++) {
            MyThread thread = new MyThread(i);
            thread.start();
        }
    }
}
class MyThread extends Thread {
    private int id;
    public MyThread(int id) {
        this.id = id;
    }
    public void run() {
        System.out.println("Thread " + id + " is running");
        try {
            Thread.sleep(1000);  // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

以上代码中,首先创建了一个ThreadDemo类,在main函数中创建了10个线程,并启动这些线程。

每个线程都是MyThread类的实例,MyThread类继承了Thread类,并重写了run()方法,在方法中模拟了一个需要执行1秒钟的任务。

在main函数中,通过创建MyThread类的实例,并调用start()方法启动线程。start()方法会调用线程的run()方法,在run()方法中执行线程的任务。

2 实现Runnable接口

另一种实现多线程的方式是实现Runnable接口,需要实现run()方法,并将实现了Runnable接口的对象传递给Thread类的构造函数。

public class RunnableDemo {
    public static void main(String[] args) {
        // 创建10个线程并启动
        for (int i = 0; i < 10; i++) {
            Runnable task = new MyTask(i);
            Thread thread = new Thread(task);
            thread.start();
        }
    }
}
class MyTask implements Runnable {
    private int id;
    public MyTask(int id) {
        this.id = id;
    }
    public void run() {
        System.out.println("Thread " + id + " is running");
        try {
            Thread.sleep(1000);  // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

以上代码中,创建了一个RunnableDemo类,在main函数中创建了10个线程,并启动这些线程。

每个线程都是MyTask类的实例,MyTask类实现了Runnable接口,并重写了run()方法,在方法中模拟了一个需要执行1秒钟的任务。


在main函数中,通过创建MyTask类的实例,并创建一个Thread对象,将Runnable对象作为参数传递给Thread构造方法,最后调用start()方法启动线程。start()方法会调用线程的run()方法,在run()方法中执行线程的任务。

在使用实现Runnable接口实现多线程时,可以更好地分离任务和线程,并提高代码的可扩展性和可维护性

如果需要添加更多的线程或任务,只需要创建更多的Runnable实例,并创建对应的Thread对象即可,不需要创建更多的线程类,并且可以更好地重用代码


3 Executor框架

Executor框架是Java提供的一个线程池框架用于管理和调度多个线程。通过Executor框架,可以更方便地实现多线程,避免手动管理线程带来的复杂性和风险。

Executor框架的核心接口是Executor和ExecutorService,


Executor是一个简单的线程池接口,只有一个execute()方法,用于提交一个Runnable任务给线程池执行。

ExecutorService是Executor的扩展接口,提供了更多的管理和调度线程的方法,如submit()、shutdown()、awaitTermination()等。


使用Executor框架实现多线程,通常需要以下步骤:

  1. 创建一个ExecutorService对象,可以使用Executors类提供的静态方法创建线程池,如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。
  2. 将需要执行的任务封装成一个Runnable或Callable对象,可以使用Java中的匿名内部类或Lambda表达式来创建。
  3. 将任务提交给ExecutorService对象执行,可以使用submit()方法提交Callable对象,或使用execute()方法提交Runnable对象。
  4. 在程序完成时,调用shutdown()方法关闭线程池,或使用awaitTermination()方法等待所有线程执行完毕。

下面是一个使用Executor框架实现多线程的示例:

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ExecutorDemo {
    public static void main(String[] args) {
        // 创建一个包含10个线程的线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        // 提交10个任务给线程池执行
        for (int i = 0; i < 10; i++) {
            executor.execute(new MyTask(i));
        }
        // 关闭线程池
        executor.shutdown();
    }
}
class MyTask implements Runnable {
    private int id;
    public MyTask(int id) {
        this.id = id;
    }
    public void run() {
        System.out.println("Thread " + id + " is running");
        try {
            Thread.sleep(1000);  // 模拟任务执行时间
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

代码讲解: 

先创建了一个ExecutorDemo类,在main函数中创建了一个包含10个线程的线程池。

每个线程池中的线程都可以执行MyTask类的实例,MyTask类实现了Runnable接口,并重写了run()方法,在方法中模拟了一个需要执行1秒钟的任务。

在main函数中,创建MyTask类的实例,并调用ExecutorService的execute()方法提交给线程池执行。

execute()方法会将任务提交给线程池中的一个空闲线程执行。

最后调用ExecutorService的shutdown()方法关闭线程池。

需要注意的是,shutdown()方法会等待所有线程执行完毕后才会关闭线程池,如果需要立即关闭线程池,可以使用shutdownNow()方法。

Callable实现多线程

Callable是Java中的一个接口,与Runnable接口类似,都用于封装一个线程执行的任务。

不同的是,Callable接口的call()方法可以返回一个结果而Runnable接口的run()方法没有返回值。

使用Callable实现多线程,通常需要以下步骤:

  1. 创建一个实现了Callable接口的类,实现call()方法,并在方法中编写线程执行的代码。
  2. 创建一个ExecutorService对象,可以使用Executors类提供的静态方法创建线程池,如newFixedThreadPool()、newCachedThreadPool()、newSingleThreadExecutor()等。
  3. 将Callable对象提交给ExecutorService对象执行,可以使用submit()方法提交。
  4. 调用Future对象的get()方法获取Callable线程执行的结果
  5. 在程序完成时,调用shutdown()方法关闭线程池,或使用awaitTermination()方法等待所有线程执行完毕。

下面是一个使用Callable实现多线程的示例:

import java.util.concurrent.*;
public class CallableDemo {
    public static void main(String[] args) throws Exception {
        // 创建一个线程池
        ExecutorService executor = Executors.newFixedThreadPool(10);
        // 提交10个Callable任务给线程池执行
        Future<Integer>[] results = new Future[10];
        for (int i = 0; i < 10; i++) {
            Callable<Integer> task = new MyTask(i);
            results[i] = executor.submit(task);
        }
        // 输出Callable任务的执行结果
        for (int i = 0; i < 10; i++) {
            Integer result = results[i].get();
            System.out.println("Task " + i + " result is " + result);
        }
        // 关闭线程池
        executor.shutdown();
    }
}
class MyTask implements Callable<Integer> {
    private int id;
    public MyTask(int id) {
        this.id = id;
    }
    public Integer call() throws Exception {
        System.out.println("Task " + id + " is running");
        Thread.sleep(1000);  // 模拟任务执行时间
        return id * 10;
    }
}

首先创建一个线程池,然后提交10个Callable任务给线程池执行。每个Callable任务都是MyTask类的实例,MyTask类实现了Callable接口,并重写了call()方法,在方法中模拟了一个需要执行1秒钟的任务,并返回一个结果。

详细解释如下:

  1. 创建一个线程池,通过调用Executors的静态方法newFixedThreadPool(10),创建了一个固定大小为10的线程池。
  2. 在for循环中,通过创建MyTask类的实例,将其封装为Callable对象,并通过ExecutorService的submit()方法提交给线程池执行。submit()方法会返回一个Future对象,代表了Callable任务的执行结果。
  3. 在for循环中,通过Future数组记录每个Callable任务的执行结果,可以通过调用get()方法获取Callable任务的执行结果。如果Callable任务还没有执行完成,get()方法会阻塞当前线程,直到任务执行完成并返回结果。如果任务执行过程中发生异常,get()方法会抛出ExecutionException异常。
  4. 在任务完成后,可以通过调用Future对象的get()方法获取任务的执行结果,并打印输出。
  5. 最后调用ExecutorService的shutdown()方法关闭线程池,应该在所有任务执行完成后才能关闭线程池。

注意,在使用Callable实现多线程时,要考虑线程安全、同步机制、任务调度和管理等问题,以确保程序的正确性和稳定性

同时,由于Callable任务的执行时间可能会比较长,可以设置超时时间来避免任务执行时间过长导致的程序阻塞。

相关文章
|
7天前
|
安全 Java API
java如何请求接口然后终止某个线程
通过本文的介绍,您应该能够理解如何在Java中请求接口并根据返回结果终止某个线程。合理使用标志位或 `interrupt`方法可以确保线程的安全终止,而处理好网络请求中的各种异常情况,可以提高程序的稳定性和可靠性。
37 6
|
16天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
16天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
40 3
|
17天前
|
监控 Java 开发者
深入理解Java中的线程池实现原理及其性能优化####
本文旨在揭示Java中线程池的核心工作机制,通过剖析其背后的设计思想与实现细节,为读者提供一份详尽的线程池性能优化指南。不同于传统的技术教程,本文将采用一种互动式探索的方式,带领大家从理论到实践,逐步揭开线程池高效管理线程资源的奥秘。无论你是Java并发编程的初学者,还是寻求性能调优技巧的资深开发者,都能在本文中找到有价值的内容。 ####
|
20天前
|
调度 开发者
核心概念解析:进程与线程的对比分析
在操作系统和计算机编程领域,进程和线程是两个基本而核心的概念。它们是程序执行和资源管理的基础,但它们之间存在显著的差异。本文将深入探讨进程与线程的区别,并分析它们在现代软件开发中的应用和重要性。
38 4
|
19天前
|
存储 算法 Java
Java内存管理深度解析####
本文深入探讨了Java虚拟机(JVM)中的内存分配与垃圾回收机制,揭示了其高效管理内存的奥秘。文章首先概述了JVM内存模型,随后详细阐述了堆、栈、方法区等关键区域的作用及管理策略。在垃圾回收部分,重点介绍了标记-清除、复制算法、标记-整理等多种回收算法的工作原理及其适用场景,并通过实际案例分析了不同GC策略对应用性能的影响。对于开发者而言,理解这些原理有助于编写出更加高效、稳定的Java应用程序。 ####
|
19天前
|
存储 监控 算法
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
|
2月前
|
存储 消息中间件 资源调度
C++ 多线程之初识多线程
这篇文章介绍了C++多线程的基本概念,包括进程和线程的定义、并发的实现方式,以及如何在C++中创建和管理线程,包括使用`std::thread`库、线程的join和detach方法,并通过示例代码展示了如何创建和使用多线程。
58 1
C++ 多线程之初识多线程
|
2月前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
27 3
|
2月前
|
Java 开发者
在Java多线程编程中,选择合适的线程创建方法至关重要
【10月更文挑战第20天】在Java多线程编程中,选择合适的线程创建方法至关重要。本文通过案例分析,探讨了继承Thread类和实现Runnable接口两种方法的优缺点及适用场景,帮助开发者做出明智的选择。
23 2

推荐镜像

更多
下一篇
DataWorks