首页 > 编程语言 >Java 并发编程:掌握多线程的四个核心要点

Java 并发编程:掌握多线程的四个核心要点

时间:2024-12-28 18:58:43浏览次数:6  
标签:Java synchronized lock void 编程 线程 多线程 public

Java 并发编程是后端开发中至关重要的一部分,它涉及到如何有效地使用多核处理器、提高程序的执行效率,并确保线程安全。无论是面试还是实际项目开发,掌握多线程编程的核心要点都至关重要。本文将围绕 Java 多线程编程的四个核心要点展开,帮助读者深入理解并发编程的基本原理、应用场景以及常见的注意事项。

一、线程的创建与启动

在 Java 中,线程是程序执行的基本单位。线程的创建和启动是进行多线程编程的第一步,掌握线程创建的方式和启动机制对理解并发编程至关重要。

1.1 线程的创建方式

Java 提供了两种常见的创建线程的方式:继承 Thread 类和实现 Runnable 接口。

1.1.1 继承 Thread

通过继承 Thread 类并重写 run() 方法来创建线程。启动线程时,调用 start() 方法。

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

public class ThreadExample {
    public static void main(String[] args) {
        MyThread thread = new MyThread();
        thread.start(); // 启动线程
    }
}
1.1.2 实现 Runnable 接口

实现 Runnable 接口比继承 Thread 类更灵活,因为 Java 允许一个类实现多个接口,但只能继承一个类。因此,推荐使用 Runnable 接口。

class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("Thread is running");
    }
}

public class ThreadExample {
    public static void main(String[] args) {
        MyRunnable myRunnable = new MyRunnable();
        Thread thread = new Thread(myRunnable);
        thread.start(); // 启动线程
    }
}

1.2 启动线程的 start() 方法

start() 方法用于启动线程,它会调用线程的 run() 方法。需要注意的是,调用 start() 后线程并不立即执行,而是进入就绪状态,具体的执行顺序由操作系统的调度器决定。

注意事项:
  • 不能直接调用 run() 方法,因为这只会在当前线程中执行 run() 方法,而不会启动一个新的线程。
  • 线程一旦结束后,不能重启。同一个线程调用 start() 多次会抛出 IllegalThreadStateException 异常。

二、线程同步机制

在多线程环境中,多个线程可能会同时访问共享资源,导致数据竞争和不一致的结果。为了解决这些问题,Java 提供了多种线程同步机制来保证线程安全。

2.1 synchronized 关键字

synchronized 是 Java 最常用的同步机制,使用它可以确保一个时间点只有一个线程可以访问某个资源。synchronized 可以用在方法上或代码块中。

2.1.1 同步方法

通过在方法声明中加上 synchronized 关键字,可以让该方法在一个时间点内只允许一个线程访问。

class Counter {
    private int count = 0;

    public synchronized void increment() {
        count++;
    }

    public synchronized int getCount() {
        return count;
    }
}
2.1.2 同步代码块

使用同步代码块可以更加精细地控制同步范围,避免整个方法都被锁住,提升程序的性能。

class Counter {
    private int count = 0;

    public void increment() {
        synchronized (this) {
            count++;
        }
    }

    public int getCount() {
        synchronized (this) {
            return count;
        }
    }
}

2.2 Lock 接口

除了 synchronized,Java 还提供了更灵活的锁机制——Lock 接口。与 synchronized 不同,Lock 提供了显式锁的控制,可以在不同的代码块之间手动获取和释放锁。

2.2.1 使用 ReentrantLock

ReentrantLockLock 接口的常用实现,它提供了更丰富的功能,比如尝试获取锁、可中断的锁等。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

class Counter {
    private int count = 0;
    private final Lock lock = new ReentrantLock();

    public void increment() {
        lock.lock(); // 获取锁
        try {
            count++;
        } finally {
            lock.unlock(); // 确保释放锁
        }
    }

    public int getCount() {
        lock.lock();
        try {
            return count;
        } finally {
            lock.unlock();
        }
    }
}
注意事项:
  • 使用 Lock 时需要显式地调用 unlock() 释放锁,最好放在 finally 块中,以确保在发生异常时也能释放锁。
  • ReentrantLock 可以实现可重入锁,即同一线程可以多次获取同一个锁。

三、线程间通信

在多线程编程中,线程往往需要进行协作和通信,以实现更复杂的功能。Java 提供了多种方式来实现线程间的通信,常见的有 wait()notify() 方法以及 Condition 接口。

3.1 wait()notify()

wait()notify()Object 类中定义的方法,必须在同步代码块或同步方法中使用。wait() 会让当前线程进入等待状态,直到其他线程调用 notify()notifyAll() 唤醒它。

3.1.1 使用 wait()notify()
class SharedResource {
    private boolean ready = false;

    public synchronized void produce() throws InterruptedException {
        while (ready) {
            wait(); // 如果资源已经准备好,则等待
        }
        ready = true;
        System.out.println("Produced");
        notify(); // 唤醒消费者线程
    }

    public synchronized void consume() throws InterruptedException {
        while (!ready) {
            wait(); // 如果资源未准备好,则等待
        }
        ready = false;
        System.out.println("Consumed");
        notify(); // 唤醒生产者线程
    }
}
3.1.2 注意事项
  • wait()notify() 必须在同步块或同步方法中使用。
  • wait() 会释放对象的锁,线程进入等待池。
  • notify() 唤醒一个等待的线程,notifyAll() 唤醒所有等待的线程。

3.2 Condition 接口

Conditionjava.util.concurrent.locks 包中的一个接口,提供了更为灵活的线程间通信机制。通过 Condition,可以在多个线程间进行更复杂的信号传递和协作。

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.Condition;

class SharedResource {
    private boolean ready = false;
    private final Lock lock = new ReentrantLock();
    private final Condition condition = lock.newCondition();

    public void produce() throws InterruptedException {
        lock.lock();
        try {
            while (ready) {
                condition.await(); // 如果资源已经准备好,等待
            }
            ready = true;
            System.out.println("Produced");
            condition.signal(); // 唤醒消费者线程
        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try {
            while (!ready) {
                condition.await(); // 如果资源未准备好,等待
            }
            ready = false;
            System.out.println("Consumed");
            condition.signal(); // 唤醒生产者线程
        } finally {
            lock.unlock();
        }
    }
}

四、线程池的使用与优化

线程池是管理和复用线程的机制,可以有效地减少线程创建和销毁的开销,并且能够更好地管理线程的生命周期。Java 提供了 ExecutorService 接口和多个实现类来管理线程池。

4.1 使用线程池

Java 提供了 Executors 工厂类来创建线程池。常见的线程池包括:

  • FixedThreadPool:固定大小的线程池。
  • CachedThreadPool:可缓存的线程池,适用于任务数量不确定的场景。
  • SingleThreadExecutor:单线程池,所有任务按顺序执行。
import java.util.concurrent.*;

public class ThreadPoolExample {
    public static void main(String[] args) throws InterruptedException, ExecutionException {
        ExecutorService executor = Executors.newFixedThreadPool(2); // 创建固定大小的线程池

        Callable<String> task = () -> {
            Thread.sleep(1000);
            return "Task finished";
        };

        Future<String> future = executor.submit(task);
        System.out.println(future.get()); // 获取任务执行结果

        executor.shutdown(); // 关闭

线程池 } }


### 4.2 线程池优化

线程池的优化主要体现在以下几个方面:

- **线程池大小的调整**:合理配置线程池的大小,避免线程过多或过少的情况。可以通过任务的数量和处理时间来调整线程池大小。
- **队列选择**:可以选择不同类型的队列(如 `LinkedBlockingQueue`、`ArrayBlockingQueue`),根据任务的特性进行优化。
- **拒绝策略**:当线程池任务队列满时,可以设置不同的拒绝策略(如 `AbortPolicy`、`CallerRunsPolicy`)。

```java
ExecutorService executor = new ThreadPoolExecutor(
    4, 10, 60, TimeUnit.SECONDS, new LinkedBlockingQueue<>(100),
    new ThreadPoolExecutor.CallerRunsPolicy() // 设置拒绝策略
);

结语

掌握 Java 并发编程的核心要点,不仅能帮助你编写高效的并发代码,还能提升你在面试中的竞争力。本文通过介绍线程的创建与启动线程同步机制线程间通信以及线程池的使用与优化,为你构建了一个完整的 Java 并发编程框架。通过不断实践和深入理解这些知识点,你将能够更好地应对各种并发编程的挑战。

标签:Java,synchronized,lock,void,编程,线程,多线程,public
From: https://blog.csdn.net/m0_38141444/article/details/144781665

相关文章

  • 【Java 线程池】详解
    线程池详解在现代的Java并发编程领域,线程池扮演着至关重要的角色。它不仅能高效地管理线程资源,避免频繁创建和销毁线程带来的性能开销,还能提升系统整体的并发处理能力与稳定性。接下来,我们将深入剖析线程池的方方面面,包括其原理、核心组成部分、使用方法以及在实际项目......
  • 【Java 并发编程】详解
    Java并发编程在当今的软件开发领域,随着多核处理器的广泛应用以及对系统性能要求的不断提高,Java并发编程变得愈发重要。它允许我们充分利用计算机的多核资源,同时处理多个任务,提高程序的执行效率和响应能力。然而,并发编程并非易事,它涉及到诸多复杂的概念、机制以及需要注......
  • Java面试知识点总结:从基础到高级的全面指南
    在准备Java面试时,系统地覆盖从基础到高级的知识点是至关重要的。以下是一个详细的Java面试知识点总结,帮助你有针对性地准备面试。也算是我自己总结的知识点,先记录下来,说不定下次准备面试的时候,能再用上。1. Java基础在Java面试中,基础知识是考核的核心部分。掌握这些基础知识......
  • Java 课程一至六章综合复习总结
    姓名:李忠学号:202402150626《Java课程一至六章综合复习总结》第一章:初始Java与面向对象程序设计核心概念:Java语言的特点,如跨平台性、面向对象、安全性等。类与对象的基本概念,包括类的定义、对象的创建和使用。知识点:Java程序的基本结构,包含package语句、import语句......
  • 202412 电子学会 图形化编程 一级真题
    2024年12月Scratch图形化编程等级考试一级真题试卷题目总数:37  总分数:100选择题第1题  单选题点击下列哪个按钮,可以将红框处的Scratch程序放大?( )A.B.C.D.第2题  单选题下列哪个按钮可以让scratch舞台区变为小舞台模式?( )A.B.C.D.......
  • BCSP-X 2024 图形化编程 小学高年级组 真题
    BCSP-X2024图形化编程小学高年级组真题题目总数:40  总分数:100选择题第1题  单选题下图为scratch声音编辑界面,以下哪个选项可以把声音的声波曲线变成一条直线?( )A.B.C.D.第2题  单选题下面哪组scratch积木可以让角色只在舞台的左......
  • 【JAVA篇】------ spring aop
    文章目录AOP(面向切面编程)前言一、AOP的概念二、AOP的核心概念三、AOP在Java中的应用场景1.整体介绍2.静态代理模式3.动态代理模式(JDK动态代理)总结AOP(面向切面编程)......
  • JAVA 7~8次题目集总结
    本次完成了7~8次的题目集是接着上次的家居强电电路模拟程序-1和家居强电电路模拟程序-2后续迭代功能拓展完成了家居强电电路模拟程序-3和家居强电电路模拟程序-4家居强电电路模拟程序-3相比较之前的升级了电路其中线路中包含多个串联起来的并联电路以及增加了新的受控电路元件......
  • Java难绷知识01——IO流的对象流
    Java难绷知识01之对象流本篇文章会探讨一些JavaIO流中比较容易被忽视的对象流,而且会相对的探讨其中的一些细节其中对于对象流的操作讲解会少一些,主要讨论的是一些细节在JavaIO流中,对象流(ObjectInputStream对象输入流和ObjectOutputStream对象输出流)用于将对象进行序列化和......
  • java三阶段总结(家用电路模拟)
    前言第六次题目集知识点:抽象类,抽象类不能被直接实例化!有抽象方法,抽象方法没有方法体,由继承它的子类提供具体实现。抽象类可以看作是一种模板,子类在实现接口方法时,必须实现接口中的所有抽象方法,除非子类也是抽象类在抽象类和接口中,多态体现为父类引用变量可以指向子类......