Java 多线程创建零基础入门新手指南:从零开始全面学习多线程创建方法

简介: 本文从零基础角度出发,深入浅出地讲解Java多线程的创建方式。内容涵盖继承`Thread`类、实现`Runnable`接口、使用`Callable`和`Future`接口以及线程池的创建与管理等核心知识点。通过代码示例与应用场景分析,帮助读者理解每种方式的特点及适用场景,理论结合实践,轻松掌握Java多线程编程 essentials。

我将从基础概念讲起,逐步深入介绍Java多线程的创建方式,通过代码示例帮助你理解,并阐述每种方式的适用场景。

Java多线程新手指南:从零开始学习多线程创建

多线程已是众人眼里老生常谈的话题之一了,在日常项目开发中它也是最为熟知且常用的技术之一,毕竟它能够允许程序同时执行多个任务,从而提高程序的响应速度和资源利用率,顾名思义,就是让计算机的多个核心同时工作,就像多个人同时做不同的任务一样。但在Java这种流行的编程语言中,多线程的使用尤为重要,因为它能帮助我们写出更高效、更流畅的程序,毕竟Java作为一种广泛应用于开发领域的语言,提供了强大的多线程支持,也提供了丰富的多线程支持,使得开发者可以轻松地实现并发编程。所以今天我继续来聊聊它,本文将重点介绍及演示如何使用Java来创建多线程,及衍生穿插知识点,带着大家能零基础轻松入门多线程编辑。

在Java多线程编程中,掌握线程的创建和管理是至关重要的。本文将介绍Java中多线程的创建方法,包括继承Thread类和实现Runnable接口两种方式,并通过源代码解析、应用场景案例、优缺点分析等来详细讲解每一种方法的使用和特点,最后通过一个测试案例来完整的复盘一遍,理论+实践 = 百分百掌握。

一、为什么需要多线程?

可能很多刚入门的小伙伴就会产生疑问,为啥需要多线程?这里我先就给大家进行分享下个人的理解,无妨大家可以这样想象一下,你在厨房里准备晚餐,如果只有你一个人,你可能需要先切菜,然后煮饭,接着炒菜;但如果你有帮手,你们可以同时进行这些任务,这样晚餐就能更快准备好。同样,在计算机程序中,如果我们能让不同的任务同时进行,程序就能更快地完成工作,也就是所谓的并行处理,同一时间多人进行,从而加快程序的执行速度。

多线程非常重要,它就像是在厨房里多请了几个帮手,每个人都能同时做不同的活儿,这样饭菜就能更快上桌。在电脑程序里,多线程能让程序同时做很多事,就像多个人一起工作一样,效率自然就高了。其具体优势如下:

  1. 提高效率:想象一下,如果电脑里只有一个程序在运行,它就只能做一件事。但有了多线程,电脑就能像多核大脑一样,同时处理好几个任务,速度自然就快了。
  2. 改善响应性:就像你在玩游戏时,如果电脑还在下载东西,游戏可能会卡顿。但如果用多线程,下载和游戏可以同时进行,互不影响,游戏就能流畅多了。
  3. 资源利用率:多线程就像是把电脑的CPU和内存都用起来,不让它们闲着。这样,电脑的每个部分都能发挥最大作用。
  4. 更好的用户体验:用户就像是顾客,他们希望点菜后能快速上菜。多线程能让程序快速响应用户的操作,就像快速上菜一样,让顾客满意。
  5. 并行处理:有些任务可以分成很多小块,每块都可以同时处理。多线程就像是有很多工人同时工作,这样完成任务的速度就快多了。
  6. 简化设计:有时候,程序设计得太复杂,就像厨房里有很多复杂的机器。多线程可以让程序设计更简单,就像用简单的工具就能做出好菜。
  7. 避免阻塞:如果一个任务卡住了,整个程序可能就会停下来。多线程可以避免这种情况,就像一个厨师忙不过来,其他厨师可以接手继续做。
  8. 利用现代硬件:现在的电脑越来越强大,多线程就像是让这些强大的电脑发挥出它们的最大能力。
  9. 适应性:在一些需要快速反应的场合,比如在线游戏或者股票交易,多线程能让程序更加灵活,快速适应变化。
  10. 错误隔离:如果程序中的一个部分出了问题,多线程可以保证其他部分不受影响,就像一个厨师出错了,其他厨师还可以继续工作。

二、Java中的多线程是如何工作的?

Java提供了两种主要的方式来创建线程,这两种方式各有特点,适用于不同的应用场景:

  1. 继承Thread类:就像你创建一个新食谱,基于一个已有的食谱进行修改。在Java中,你可以通过创建一个新的类,继承自Thread类,并重写它的run方法来定义你的任务。
  2. 实现Runnable接口:这就像是按照食谱做菜,你不需要自己从头开始创造食谱,只需要按照已有的步骤来。在Java中,你可以创建一个实现了Runnable接口的类,并实现它的run方法,然后将其传递给Thread对象。

三、如何创建和启动线程?

创建和启动线程是多线程编程中的基础操作,它们确保了程序能够并行执行多个任务。以下是创建和启动线程的详细步骤,以及它们之间的逻辑联系:

  1. 定义任务:在多线程编程中,每个线程都需要执行一个特定的任务。任务的定义通常通过编写一个run方法来实现。这个方法是线程执行的入口点,包含了线程要执行的所有操作。
  2. 选择线程创建方式:根据你的程序需求,你可以选择继承Thread类或实现Runnable接口来定义你的任务。继承Thread类意味着你的类直接扩展了线程的功能,而实现Runnable接口则需要将你的任务类传递给一个Thread对象。
  3. 创建线程对象:一旦你定义了任务,接下来就是创建线程对象。如果你选择继承Thread类,你将创建该类的实例;如果实现Runnable接口,你需要创建一个Runnable对象,并将该对象作为参数传递给Thread类的构造函数,然后创建Thread对象的实例。
  4. 启动线程:创建线程对象后,你需要调用start()方法来启动线程。这个方法会触发线程的执行,使其进入就绪状态,并最终运行。重要的是要注意,start()方法会隐式地调用你的run方法,因此无需手动调用run
  5. 线程的执行:一旦线程启动,它将按照定义的run方法中的指令执行任务。线程的执行是由Java运行时环境的线程调度器控制的,它会根据系统的线程调度策略来决定哪个线程何时执行。

四、Java多线程创建方式详解

4.1 继承Thread类

这是创建线程最直接的方式之一。通过继承Thread类,并重写其run方法,来定义线程的执行逻辑。

4.1.1 创建步骤

  1. 定义线程类:创建一个类并继承自Thread类。在这个类中,重写run()方法,run()方法中的代码就是这个线程要执行的任务。
    class MyThread extends Thread {
         
     @Override
     public void run() {
         
         for (int i = 0; i < 5; i++) {
         
             System.out.println("线程 " + getName() + " 执行,i = " + i);
         }
     }
    }
    
  2. 创建并启动线程:在主程序中创建线程对象,并调用start()方法来启动线程。start()方法会自动调用线程对象的run()方法,使得线程开始执行。例如:

    public class Main {
         
     public static void main(String[] args) {
         
         MyThread thread1 = new MyThread();
         thread1.start();
    
         MyThread thread2 = new MyThread();
         thread2.start();
     }
    }
    

    4.1.2 优缺点分析

  • 优点:简单直接,符合面向对象的编程思想,初学者容易理解和掌握。
  • 缺点:由于Java是单继承的,如果一个类已经继承了其他类,就无法再继承Thread类来创建线程,这限制了类的扩展性。同时,线程与任务逻辑耦合在一起,不符合“组合优于继承”的设计原则。

4.1.3 应用场景案例

适合一些简单的、独立的任务,不需要继承其他类的情况。比如,在一个小型的测试程序中,需要创建几个简单的线程来模拟并发操作,此时使用继承Thread类的方式就比较合适。

4.2 实现Runnable接口

这是另一种常见的创建线程的方式,通过实现Runnable接口来定义任务,然后将其传递给Thread对象。

4.2.1 创建步骤

  1. 定义实现Runnable接口的类:创建一个类实现Runnable接口,然后实现接口中的run()方法,将线程要执行的任务写在run()方法中。例如:
    class MyRunnable implements Runnable {
         
     @Override
     public void run() {
         
         for (int i = 0; i < 5; i++) {
         
             System.out.println("线程 " + Thread.currentThread().getName() + " 执行,i = " + i);
         }
     }
    }
    
  2. 创建并启动线程:首先创建实现了Runnable接口的类的对象,然后将这个对象作为参数传递给Thread类的构造函数来创建线程对象,最后调用start()方法启动线程。例如:

    public class Main {
         
     public static void main(String[] args) {
         
         MyRunnable runnable1 = new MyRunnable();
         Thread thread1 = new Thread(runnable1);
         thread1.start();
    
         MyRunnable runnable2 = new MyRunnable();
         Thread thread2 = new Thread(runnable2);
         thread2.start();
     }
    }
    

    4.2.2 优缺点分析

  • 优点:避免了单继承的限制,因为一个类可以实现多个接口。同时,多个线程可以共享同一个Runnable对象,适合多个线程执行相同任务的情况,提高了代码的复用性。任务与线程分离,更符合“组合优于继承”的设计原则,使得代码的结构更加清晰。
  • 缺点:相比继承Thread类,代码稍微复杂一些,需要创建Runnable对象并将其传递给Thread对象。

4.2.3 应用场景案例

在多线程的场景中,如果有多个线程需要执行相同的任务逻辑,例如在一个服务器程序中,多个客户端请求可能需要执行相同的业务逻辑,此时使用实现Runnable接口的方式就非常合适。可以创建一个Runnable实现类,然后为每个客户端请求创建一个新的线程来执行该Runnable任务。

4.3 使用Callable和Future接口(适用于有返回值的线程)

前面两种方式创建的线程,其执行结果是无法直接获取的。如果需要获取线程执行后的返回值,可以使用CallableFuture接口。

4.3.1 创建步骤

  1. 定义实现Callable接口的类:创建一个类实现Callable接口,需要指定返回值类型。实现call()方法,在这个方法中编写线程要执行的任务,并返回一个结果。例如,计算1到100的整数和:
    ```java
    import java.util.concurrent.Callable;

class MyCallable implements Callable {
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = 1; i <= 100; i++) {
sum += i;
}
return sum;
}
}

2. **创建并执行线程,获取结果**:通过`ExecutorService`来管理线程的执行。首先创建`Callable`对象,然后将其提交给`ExecutorService`,返回一个`Future`对象。通过`Future`对象可以获取线程执行后的返回值。例如:
```java
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class Main {
    public static void main(String[] args) throws Exception {
        MyCallable callable = new MyCallable();
        // 创建一个线程池,这里只使用一个线程来执行任务,实际可以根据需求调整线程数量
        ExecutorService executor = Executors.newSingleThreadExecutor();
        Future<Integer> future = executor.submit(callable);
        // 获取线程执行后的结果
        Integer result = future.get();
        System.out.println("1到100的整数和为:" + result);
        // 关闭线程池
        executor.shutdown();
    }
}

4.3.2 优缺点分析

  • 优点:可以获取线程执行后的返回值,并且可以通过ExecutorService来更好地管理线程,如设置线程池大小、控制线程的执行顺序等,提高了线程的管理和控制能力。
  • 缺点:相比前两种方式,代码更加复杂,需要理解ExecutorServiceCallableFuture等多个接口和类的使用,对初学者来说难度较大。

4.3.3 应用场景案例

在一些需要异步计算并获取结果的场景中非常有用。比如,在一个数据分析程序中,需要启动一个线程去执行复杂的数据计算任务,计算完成后获取计算结果进行后续处理。此时使用CallableFuture接口就可以满足需求。

4.4 使用线程池创建多线程

线程池是一种管理和复用线程的机制,可以避免频繁创建和销毁线程带来的开销,提高系统性能。

4.4.1 使用ExecutorService和Executors创建线程池

  1. 创建线程池:可以使用Executors工厂类来创建不同类型的线程池。例如,创建一个固定大小的线程池:
    // 创建一个包含5个线程的固定大小线程池
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    
    Executors还可以创建其他类型的线程池,如CachedThreadPool(根据需要创建线程,线程空闲一段时间后会被回收)、SingleThreadExecutor(只有一个线程的线程池)等。
  2. 定义任务(可以是实现Runnable接口或Callable接口的类):例如,定义一个实现Runnable接口的任务类:
    class MyTask implements Runnable {
         
     @Override
     public void run() {
         
         System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行任务");
     }
    }
    
  3. 提交任务到线程池:将任务提交给线程池执行。对于实现Runnable接口的任务,可以使用execute()方法提交;对于实现Callable接口的任务(用于有返回值的情况),可以使用submit()方法提交。以下是提交Runnable任务的示例:
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    // 创建任务对象
    MyTask task = new MyTask();
    // 提交任务到线程池,这个任务会被线程池中的某个线程执行
    executorService.execute(task);
    // 关闭线程池,不再接受新任务,但会等待已提交的任务执行完毕
    executorService.shutdown();
    
  4. 关闭线程池(可选):当任务都提交完毕后,可以关闭线程池。shutdown()方法会平滑地关闭线程池,它会等待所有已提交的任务执行完毕后再关闭线程池。如果希望立即关闭线程池,不等待未完成的任务,可以使用shutdownNow()方法,但这种方式可能会导致正在执行的任务被中断。

4.4.2 自定义线程池(使用ThreadPoolExecutor类)

除了使用Executors工厂类创建线程池外,还可以通过ThreadPoolExecutor类来自定义线程池的参数,以满足更复杂的需求。

import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Main {
   
    public static void main(String[] args) {
   
        // 核心线程数
        int corePoolSize = 2;
        // 最大线程数
        int maximumPoolSize = 4;
        // 线程空闲时间
        long keepAliveTime = 10;
        // 时间单位
        TimeUnit unit = TimeUnit.SECONDS;
        // 任务队列
        BlockingQueue<Runnable> workQueue = new LinkedBlockingQueue<>(10);

        ThreadPoolExecutor executor = new ThreadPoolExecutor(
                corePoolSize,
                maximumPoolSize,
                keepAliveTime,
                unit,
                workQueue);

        for (int i = 0; i < 15; i++) {
   
            final int taskNumber = i;
            executor.submit(() -> {
   
                System.out.println("线程 " + Thread.currentThread().getName() + " 正在执行任务 " + taskNumber);
                try {
   
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
   
                    e.printStackTrace();
                }
            });
        }

        executor.shutdown();
    }
}

在这个示例中,我们创建了一个自定义的线程池,通过ThreadPoolExecutor的构造函数设置了核心线程数、最大线程数、线程空闲时间、任务队列等参数。

4.4.3 线程池的优缺点分析

  • 优点
    • 提高性能:避免了频繁创建和销毁线程带来的开销,线程池中的线程可以复用,减少了线程创建和销毁的时间和资源消耗。
    • 控制并发数:可以通过设置线程池的大小来控制并发执行的线程数量,避免过多的线程竞争

Java, 多线程,线程创建,零基础入门,新手教程,并发编程,线程池,Thread 类,Runnable 接口,Callable 接口,Future, 多线程同步,volatile, 锁机制,Java 多线程实战



资源地址:
https://pan.quark.cn/s/14fcf913bae6


相关文章
|
18天前
|
前端开发 JavaScript Java
Java 开发中 Swing 界面嵌入浏览器实现方法详解
摘要:Java中嵌入浏览器可通过多种技术实现:1) JCEF框架利用Chromium内核,适合复杂网页;2) JEditorPane组件支持简单HTML显示,但功能有限;3) DJNativeSwing-SWT可内嵌浏览器,需特定内核支持;4) JavaFX WebView结合Swing可完美支持现代网页技术。每种方案各有特点,开发者需根据项目需求选择合适方法,如JCEF适合高性能要求,JEditorPane适合简单展示。(149字)
77 1
|
4天前
|
并行计算 Java API
Java 基础篇完整学习攻略
本教程涵盖Java基础到高级内容,包括模块化系统、Stream API、多线程编程、JVM机制、集合框架及新特性如Records和模式匹配等,适合零基础学员系统学习Java编程。
19 0
|
8天前
|
安全 算法 Java
Java 多线程:线程安全与同步控制的深度解析
本文介绍了 Java 多线程开发的关键技术,涵盖线程的创建与启动、线程安全问题及其解决方案,包括 synchronized 关键字、原子类和线程间通信机制。通过示例代码讲解了多线程编程中的常见问题与优化方法,帮助开发者提升程序性能与稳定性。
46 0
|
6天前
|
Java API 调度
从阻塞到畅通:Java虚拟线程开启并发新纪元
从阻塞到畅通:Java虚拟线程开启并发新纪元
148 83
|
13天前
|
存储 Java 调度
Java虚拟线程:轻量级并发的革命性突破
Java虚拟线程:轻量级并发的革命性突破
154 83
|
9天前
|
Java 索引
Java ArrayList中的常见删除操作及方法详解。
通过这些方法,Java `ArrayList` 提供了灵活而强大的操作来处理元素的移除,这些方法能够满足不同场景下的需求。
69 30
|
2天前
|
安全 Java API
Java 17 及以上版本核心特性在现代开发实践中的深度应用与高效实践方法 Java 开发实践
本项目以“学生成绩管理系统”为例,深入实践Java 17+核心特性与现代开发技术。采用Spring Boot 3.1、WebFlux、R2DBC等构建响应式应用,结合Record类、模式匹配、Stream优化等新特性提升代码质量。涵盖容器化部署(Docker)、自动化测试、性能优化及安全加固,全面展示Java最新技术在实际项目中的应用,助力开发者掌握现代化Java开发方法。
18 1
|
29天前
|
Java 数据库 数据安全/隐私保护
银行流水生成器在线制作,银行转账p图在线生成,java实现最牛的生成器【仅供学习用途】
本资料探讨银行系统核心技术,涵盖交易记录生成、电子回单加密验真及基于Java的财务管理系统开发。主要内容包括:交易记录实体类设计(不可变性与数字签名)
|
22天前
|
缓存 Java 数据库
Java 访问修饰符使用方法与组件封装方法详细说明
本文详细介绍了Java中访问修饰符(`public`、`private`、`protected`、默认)的使用方法,并结合代码示例讲解了组件封装的核心思想与实现技巧。内容涵盖数据封装、继承扩展、模块化设计与接口隔离等关键技术点,帮助开发者提升代码的可维护性与安全性,适用于Java初学者及进阶开发者学习参考。
30 1