40、Java 并发编程基础 ①

简介: 40、Java 并发编程基础 ①

一、进程(Process)

  • 进程:操作系统中运行的应用程序

  • 进程与进程之间是独立的
  • 每个进程都运行在其专用且受保护的内存空间中

二、线程(Thread)

  • 一个进程要想执行任务就必须要有线程(一个进程至少要有一个线程
  • 一个进程中的所有任务都在线程中执行

三、线程的串行

  • 线程中任务的执行是串行
  • 若要在一个线程中执行多个任务,那么只能一个一个地按顺序地执行这些任务
  • 同一时间内,一个线程只能执行一个任务

四、多线程

  • 一个进程中可以开启多个线程、所有线程并行(同时)执行不同的任务
  • 进程 👉 车间;线程 👉 车间工人
  • 多线程技术可以提高程序的执行效率

五、多线程原理

  • 同一时间,CPU 的一个核心只能处理一个线程(只有一个线程在工作)
  • 多线程并发(同时)执行,其实是 CPU 快速地在多个线程之间调度(切换)

  • 如果 CPU 调度线程的速度足够快,就造成了多条线程并发执行的假象
  • 如果是多核 CPU,才是真正地实现了多个线程同时执行
  • 若线程非常多:① CPU 将在多个线程之间来回调度,效率大量 CPU 资源;② 每条线程被调度执行地频次会降低(线程的执行效率会降低)

六、多线程优缺点

  • 优点:① 能适当提高线程的执行效率;② 能适当提高资源利用率(CPU、内存利用率)
  • 缺点:① 开启线程会占用一定的内存空间。若开启大量线程,会占用大量的内存空间,降低程序性能;② 线程越多,CPU 在调用线程上的开销越大

七、Java 的默认线程

  • 每个 Java 程序启动后会默认开启一个线程,称为主线程(main 方法所在线程)
  • 每一条线程都是一个 java.lang.Thread 对象,可通过 Thread.currentThread 方法获取当前线程的线程(Thread) 对象

八、开启新线程

(1) new Thread()

public class QQTest {
    public static void main(String[] args) {
        // ① 创建了线程对象 musicThread
        Thread musicThread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 这条线程中要执行的任务
                System.out.println("\n播放音乐");
                System.out.println(Thread.currentThread());
            }
        });
        musicThread.setName("Thread-music");
        musicThread.setPriority(10);
        // ② 开启 musicThread 线程
        musicThread.start();
    }
}

(2) 继承 Thread,重写 run 方法

public class QQTest {
    public static void main(String[] args) {
        MusicThread musicThread = new MusicThread();
        musicThread.setName("Music-Thread");
        musicThread.start();
    }
}
class MusicThread extends Thread {
    @Override
    public void run() {
        // 该线程要执行的任务
        System.out.println(getName() + "_播放音乐");
    }
}

(3) run() 和 start()

public class QQTest {
    public static void main(String[] args) {
        Thread newThread = new Thread(() -> {
            System.out.println(Thread.currentThread());
        });
        // Thread[Thread-0,5,main]
        newThread.start();
        // Thread[main,5,main]
        newThread.run();
    }
}

九、多线程的内存布局

PC 寄存器(Program Counter Register):每个线程都有自己的 PC 寄存器。

PC 寄存器记录着 JVM 正在执行哪一条语句

Java 虚拟机栈(Java Virtual Machine Stack):每个线程都有自己的 Java 虚拟机栈

(Heap):多个线程共享堆空间

方法区(Method Area):多个线程共享方法区

方法区放代码

本地方法栈(Native Method Stack):每个线程都有自己的本地方法栈

十、线程的六种状态

  • 可通过 Thread 对象的 getState() 获得线程的状态

① NEW(新建):尚未启动

② RUNNABLE(可运行状态):正在 JVM 中运行【或正在等待操作系统的其他资源(如:处理器)】

③ BLOCKED(阻塞状态):正在等待监视器锁(内部锁)

④ WAITING(等待状态):在等待另一个线程

调用以下方法会进入 WAITING 状态:

✏️ 没有超时值的:Object.wait

✏️ 没有超时值的:Thread.join

✏️ LockSupport.park

⑤ TIMED_WAITING(定时等待状态)

调用以下方法会进入 TIMED_WAITING状态:

✏️ Thread.sleep

✏️ 有超时值的:Object.wait

✏️ 有超时值的:Thread.join

✏️ LockSupport.parkNanos

✏️ LockSupport.parkUntil

⑥ TERMINATED(终止状态):已经执行完毕

public class QQTest {
    public static void main(String[] args) {
        // 主线程对象
        Thread mainThread = Thread.currentThread();
        // RUNNABLE
        System.out.println(mainThread.getState().name());
        // 子线程
        Thread subThread = new Thread();
        // NEW
        System.out.println(subThread.getState().name());
    }
}

十一、sleep、interrupt

  • 可通过 Thread.sleep 方法暂停(睡眠)当前线程,进入 WAITING 状态
  • 在暂停(睡眠)期间,若调用线程对象的 interrupt 方法中断线程,会抛出 java.lang.InterruptedException
public class QQTest {
    public static void main(String[] args) {
        Thread subThread = new Thread(() -> {
            System.out.println("666");
            try {
                Thread.sleep(3333);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("520");
        });
        subThread.start();
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("111");
        subThread.interrupt();
    }
}

相关文章
|
1月前
|
Java 开发者
Java多线程编程中的常见误区与最佳实践####
本文深入剖析了Java多线程编程中开发者常遇到的几个典型误区,如对`start()`与`run()`方法的混淆使用、忽视线程安全问题、错误处理未同步的共享变量等,并针对这些问题提出了具体的解决方案和最佳实践。通过实例代码对比,直观展示了正确与错误的实现方式,旨在帮助读者构建更加健壮、高效的多线程应用程序。 ####
|
17天前
|
安全 Java 程序员
深入理解Java内存模型与并发编程####
本文旨在探讨Java内存模型(JMM)的复杂性及其对并发编程的影响,不同于传统的摘要形式,本文将以一个实际案例为引子,逐步揭示JMM的核心概念,包括原子性、可见性、有序性,以及这些特性在多线程环境下的具体表现。通过对比分析不同并发工具类的应用,如synchronized、volatile关键字、Lock接口及其实现等,本文将展示如何在实践中有效利用JMM来设计高效且安全的并发程序。最后,还将简要介绍Java 8及更高版本中引入的新特性,如StampedLock,以及它们如何进一步优化多线程编程模型。 ####
21 0
|
19天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界中,异常处理是代码健壮性的守护神。本文将带你从异常的基本概念出发,逐步深入到高级用法,探索如何优雅地处理程序中的错误和异常情况。通过实际案例,我们将一起学习如何编写更可靠、更易于维护的Java代码。准备好了吗?让我们一起踏上这段旅程,解锁Java异常处理的秘密!
|
3天前
|
算法 Java 调度
java并发编程中Monitor里的waitSet和EntryList都是做什么的
在Java并发编程中,Monitor内部包含两个重要队列:等待集(Wait Set)和入口列表(Entry List)。Wait Set用于线程的条件等待和协作,线程调用`wait()`后进入此集合,通过`notify()`或`notifyAll()`唤醒。Entry List则管理锁的竞争,未能获取锁的线程在此排队,等待锁释放后重新竞争。理解两者区别有助于设计高效的多线程程序。 - **Wait Set**:线程调用`wait()`后进入,等待条件满足被唤醒,需重新竞争锁。 - **Entry List**:多个线程竞争锁时,未获锁的线程在此排队,等待锁释放后获取锁继续执行。
27 12
|
23天前
|
设计模式 Java 开发者
Java多线程编程的陷阱与解决方案####
本文深入探讨了Java多线程编程中常见的问题及其解决策略。通过分析竞态条件、死锁、活锁等典型场景,并结合代码示例和实用技巧,帮助开发者有效避免这些陷阱,提升并发程序的稳定性和性能。 ####
|
23天前
|
缓存 Java 开发者
Java多线程编程的陷阱与最佳实践####
本文深入探讨了Java多线程编程中常见的陷阱,如竞态条件、死锁和内存一致性错误,并提供了实用的避免策略。通过分析典型错误案例,本文旨在帮助开发者更好地理解和掌握多线程环境下的编程技巧,从而提升并发程序的稳定性和性能。 ####
|
16天前
|
安全 算法 Java
Java多线程编程中的陷阱与最佳实践####
本文探讨了Java多线程编程中常见的陷阱,并介绍了如何通过最佳实践来避免这些问题。我们将从基础概念入手,逐步深入到具体的代码示例,帮助开发者更好地理解和应用多线程技术。无论是初学者还是有经验的开发者,都能从中获得有价值的见解和建议。 ####
|
16天前
|
Java 调度
Java中的多线程编程与并发控制
本文深入探讨了Java编程语言中多线程编程的基础知识和并发控制机制。文章首先介绍了多线程的基本概念,包括线程的定义、生命周期以及在Java中创建和管理线程的方法。接着,详细讲解了Java提供的同步机制,如synchronized关键字、wait()和notify()方法等,以及如何通过这些机制实现线程间的协调与通信。最后,本文还讨论了一些常见的并发问题,例如死锁、竞态条件等,并提供了相应的解决策略。
40 3
|
22天前
|
缓存 Java 开发者
Java多线程并发编程:同步机制与实践应用
本文深入探讨Java多线程中的同步机制,分析了多线程并发带来的数据不一致等问题,详细介绍了`synchronized`关键字、`ReentrantLock`显式锁及`ReentrantReadWriteLock`读写锁的应用,结合代码示例展示了如何有效解决竞态条件,提升程序性能与稳定性。
84 6
|
26天前
|
Java 程序员
Java编程中的异常处理:从基础到高级
在Java的世界里,异常是程序运行中不可忽视的“惊喜”。它们可能突如其来,也可能悄无声息地潜伏。掌握异常处理的艺术,意味着你能够优雅地面对程序的不完美,并确保它即使在风雨飘摇中也能继续航行。本文将引导你理解Java异常的本质,探索捕获和处理这些异常的方法,并最终学会如何利用自定义异常为你的代码增添力量。