深入浅出Java并发编程:线程基础

🧑 博主简介:CSDN博客专家历代文学网 (PC端可以访问:https://literature.sinhy.com/#/?__c=1000,移动端可微信小程序搜索"历代文学 ")总架构师,15年工作经验,精通Java编程高并发设计Springboot和微服务,熟悉LinuxESXI虚拟化以及云原生Docker和K8s,热衷于探索科技的边界,并将理论知识转化为实际应用。保持对新技术的好奇心,乐于分享所学,希望通过我的实践经历和见解,启发他人的创新思维。在这里,我希望能与志同道合的朋友交流探讨,共同进步,一起在技术的世界里不断学习成长。
技术合作 请加本人wx(注明来自csdn ):foreast_sea


深入浅出Java并发编程:线程基础

引言

在当今的软件开发领域,并发编程已经成为一项不可或缺的技能。随着多核处理器的普及,应用程序的性能优化越来越依赖于如何有效地利用多线程技术。Java作为一门成熟的编程语言,提供了丰富的并发编程工具和API,使得开发者能够轻松地构建高效、稳定的多线程应用。

然而,并发编程并非易事。它涉及到许多复杂的概念和技术,如线程安全锁机制线程通信等。对于初学者来说,理解这些概念并掌握其应用是一个不小的挑战。本文将从最基础的线程概念入手,逐步深入,帮助读者建立起对Java并发编程的全面理解。

本文将围绕以下几个核心主题展开:

  1. 进程与线程的区别与联系:理解操作系统层面的进程与线程,以及它们在Java中的具体表现。
  2. 线程的创建方式 :详细介绍Java中创建线程的三种方式:继承Thread类、实现Runnable接口、以及使用CallableFuture
  3. 线程的生命周期与状态转换:深入探讨线程从创建到销毁的整个生命周期,以及各个状态之间的转换条件。
  4. 守护线程与用户线程:解释守护线程与用户线程的区别,以及它们在应用中的使用场景。
  5. 线程优先级与调度策略:探讨线程优先级的概念,以及Java虚拟机如何调度线程。

1. 进程与线程的区别与联系

1.1 进程与线程的基本概念

在操作系统中,进程线程是两个核心概念。理解它们的区别与联系是学习并发编程的基础。

  • 进程:进程是操作系统进行资源分配和调度的基本单位。每个进程都有独立的内存空间,包含了程序代码、数据、堆栈等。进程之间的通信需要通过特定的机制,如管道、消息队列、共享内存等。

  • 线程:线程是进程中的一个执行单元,是CPU调度的基本单位。一个进程可以包含多个线程,这些线程共享进程的内存空间和资源。线程之间的通信相对简单,因为它们可以直接访问共享的内存。

1.2 进程与线程的区别

特性 进程 线程
资源分配 独立的内存空间 共享进程的内存空间
通信方式 需要特定的机制(如管道、消息队列) 可以直接访问共享内存
创建开销 较大 较小
切换开销 较大 较小
独立性 高度独立 依赖于进程

1.3 进程与线程的联系

  • 资源共享:线程共享进程的资源,如内存、文件句柄等。这使得线程之间的通信更加高效。
  • 并发执行:多个线程可以在同一个进程中并发执行,从而提高程序的执行效率。
  • 依赖关系:线程依赖于进程,进程终止时,其所有线程也会终止。

2. 线程的创建方式

在Java中,创建线程主要有三种方式:继承Thread类、实现Runnable接口、以及使用CallableFuture。下面我们将详细介绍这三种方式。

2.1 继承Thread

继承Thread类是最简单的创建线程的方式。通过重写run()方法,可以定义线程执行的任务。

java 复制代码
class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread is running");
    }
}

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

优点:简单直观,适合简单的任务。

缺点 :由于Java不支持多继承,继承Thread类后无法再继承其他类。

2.2 实现Runnable接口

实现Runnable接口是更常用的创建线程的方式。通过实现run()方法,可以将任务与线程分离。

java 复制代码
class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable is running");
    }
}

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

优点:避免了单继承的限制,适合复杂的任务。

缺点:无法直接获取线程的执行结果。

2.3 使用CallableFuture

Callable接口与Runnable接口类似,但它可以返回一个结果,并且可以抛出异常。通过Future对象,可以获取线程的执行结果。

java 复制代码
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;

class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        return "Callable is running";
    }
}

public class Main {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        FutureTask<String> futureTask = new FutureTask<>(new MyCallable());
        Thread thread = new Thread(futureTask);
        thread.start();
        System.out.println(futureTask.get());
    }
}

优点:可以获取线程的执行结果,适合需要返回值的任务。

缺点 :使用相对复杂,需要处理Future对象。

3. 线程的生命周期与状态转换

线程的生命周期包括多个状态,理解这些状态及其转换条件对于掌握线程的行为至关重要。

3.1 线程的生命周期

Java线程的生命周期包括以下几个状态:

  • 新建(New):线程对象被创建,但尚未启动。
  • 就绪(Runnable):线程已经启动,等待CPU调度执行。
  • 运行(Running) :线程正在执行run()方法。
  • 阻塞(Blocked):线程因为某些原因(如等待锁)暂时停止执行。
  • 等待(Waiting):线程无限期等待其他线程的通知。
  • 超时等待(Timed Waiting):线程在指定的时间内等待其他线程的通知。
  • 终止(Terminated):线程执行完毕或被强制终止。

3.2 状态转换

线程的状态转换可以通过以下方法触发:

  • start():将线程从新建状态转换为就绪状态。
  • yield():将线程从运行状态转换为就绪状态。
  • sleep():将线程从运行状态转换为超时等待状态。
  • wait():将线程从运行状态转换为等待状态。
  • notify()/notifyAll():将线程从等待状态转换为就绪状态。
  • join():将线程从运行状态转换为等待状态,直到目标线程终止。
  • interrupt():将线程从阻塞或等待状态转换为就绪状态。

3.3 状态跃迁图谱

start() run()结束 竞争锁失败 获取到锁 wait()/join() notify()/notifyAll() sleep(n)/wait(n) 超时结束 NEW RUNNABLE TERMINATED BLOCKED WAITING TIMED_WAITING

4. 守护线程与用户线程

特性对比

特征项 用户线程 守护线程
JVM退出条件 全部终止 不阻止退出
默认值
典型应用 业务逻辑 GC线程
异常处理 向上传播 静默失败

4.1 守护线程

守护线程是一种特殊的线程,它在后台运行,为其他线程提供服务。当所有的用户线程结束时,守护线程会自动终止。

java 复制代码
class DaemonThread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("Daemon thread is running");
        }
    }
}

public class Main {
    public static void main(String[] args) {
        DaemonThread daemonThread = new DaemonThread();
        daemonThread.setDaemon(true);
        daemonThread.start();
        System.out.println("Main thread is finished");
    }
}

特点

  • 守护线程不会阻止JVM退出。
  • 守护线程通常用于执行一些后台任务,如垃圾回收、日志记录等。

4.2 用户线程

用户线程是普通的线程,它的生命周期与应用程序的生命周期一致。只有当所有的用户线程结束时,JVM才会退出。

特点

  • 用户线程的执行会影响应用程序的生命周期。
  • 用户线程通常用于执行应用程序的核心任务。

5. 线程优先级与调度策略

5.1 优先级失效实验

java 复制代码
IntStream.rangeClosed(1, 10).forEach(i -> {
    Thread thread = new Thread(() -> {
        long count = 0;
        while (!Thread.interrupted()) {
            count++;
        }
        System.out.println(Thread.currentThread().getName() + ": " + count);
    });
    thread.setPriority(i % 2 == 0 ? Thread.MAX_PRIORITY : Thread.MIN_PRIORITY);
    thread.start();
});
// 观察输出结果的无序性

5.2 线程优先级

Java中的线程优先级分为10个级别,范围从1(最低)到10(最高)。默认情况下,线程的优先级为5。

java 复制代码
class PriorityThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread priority: " + getPriority());
    }
}

public class Main {
    public static void main(String[] args) {
        PriorityThread thread1 = new PriorityThread();
        PriorityThread thread2 = new PriorityThread();
        thread1.setPriority(Thread.MIN_PRIORITY);
        thread2.setPriority(Thread.MAX_PRIORITY);
        thread1.start();
        thread2.start();
    }
}

注意:线程优先级只是一个提示,具体的调度策略由JVM和操作系统决定。

5.3 线程调度策略

Java的线程调度策略主要依赖于操作系统的调度算法。常见的调度策略包括:

  • 时间片轮转调度:每个线程分配一个时间片,时间片用完后切换到下一个线程。
  • 优先级调度:高优先级的线程优先执行。
  • 抢占式调度:高优先级的线程可以抢占低优先级线程的执行权。

注意:Java的线程调度是非确定性的,开发者不应依赖线程优先级来控制程序的执行顺序。

结语

通过本文的学习,我们详细探讨了Java并发编程中的线程基础,包括进程与线程 的区别、线程的创建方式线程的生命周期守护线程用户线程、以及线程优先级与调度策略。掌握这些基础知识是进一步学习并发编程的关键。

在实际开发中,理解并正确使用这些概念可以帮助我们构建高效、稳定的多线程应用。然而,并发编程的复杂性远不止于此,后续我们还将深入探讨线程安全、锁机制、线程通信等高级主题。

参考资料

  1. Java Concurrency in Practice - Brian Goetz
  2. Oracle Java Documentation
  3. Java Threads and the Concurrency Utilities - Jeff Friesen
  4. Java并发编程实战 - 方腾飞
相关推荐
dengjiayue2 分钟前
golang 高性能的 MySQL 数据导出
开发语言·mysql·golang
折枝寄北7 分钟前
从零开始 | C语言基础刷题DAY1
c语言·开发语言·算法
简 洁 冬冬28 分钟前
java中过滤器
java
V+zmm1013440 分钟前
电器维修系统小程序+论文源码调试讲解
java·数据库·微信小程序·小程序·毕业设计
PawSQL1 小时前
推理模型对SQL理解能力的评测:DeepSeek r1、GPT-4o、Kimi k1.5和Claude 3.7 Sonnet
java·数据库·人工智能·sql·sql优化·pawsql·deepseek
BeanInJ1 小时前
JAVA字符串与正则表达式
java·正则表达式
weixin_307779131 小时前
PyTorch调试与错误定位技术
开发语言·人工智能·pytorch·python·深度学习
珹洺1 小时前
数据库系统概论(三)数据库系统的三级模式结构
java·运维·服务器·数据库·oracle
*.✧屠苏隐遥(ノ◕ヮ◕)ノ*.✧1 小时前
C语言_数据结构总结4:不带头结点的单链表
c语言·开发语言·数据结构·算法·链表·visualstudio·visual studio
极客代码2 小时前
Linux IPC:System V共享内存汇总整理
linux·c语言·开发语言·并发·共享内存·通信·system v