Java多线程并发控制实战:同步、生产者消费者与Callable应用

Chloe
Chloe
发布时间:2026-10-15| 2人浏览过
原创

本文通过数字加减、生产搬运电脑及竞拍抢答三个综合案例,深入解析Java多线程中的`synchronized`关键字、`wait()`与`notifyAll()`的协作机制,以及`Callable`接口在异步任务中的应用。旨在帮助开发者理解线程间的资源竞争、锁释放与唤醒流程,掌握高并发环境下的基础编程技巧。

数字加减:互斥同步与等待唤醒

此案例的核心在于解决两个加法线程与两个减法线程对共享资源 `num` 的并发访问问题。关键在于保证数据变化的原子性与一致性:执行一次加操作后必须紧接着执行减操作(或反之),使得最终结果仅在 0、-1、1 之间循环波动。

实现方案采用类级别的 synchronized 方法配合布尔标志位 flag 进行状态切换:

  • 加法操作 (add()):若标志位为 false(表示正在进行减法),线程调用 wait() 释放锁并等待;否则执行加1操作,设置 flag=false,调用 notifyAll() 唤醒所有等待线程。
  • 减法操作 (sub()):逻辑相反,若标志位为 true 则等待;执行减1后设置 flag=true 并唤醒其他线程。
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Resource res = new Resource();
        AddThread at = new AddThread(res);
        SubThread st = new SubThread(res);
        // 启动两个加法线程和两个减法线程
        new Thread(at, "加法线程A:").start();
        new Thread(at, "加法线程B:").start();
        new Thread(st, "减法线程X:").start();
        new Thread(st, "减法线程Y:").start();
    }
}

class AddThread implements Runnable {
    private Resource resource;
    public AddThread(Resource resource) { this.resource = resource; }
    @Override
    public void run() {
        for (int x = 0; x < 50; x++) {
            try { resource.add(); } catch (Exception e) { e.printStackTrace(); }
        }
    }
}

class SubThread implements Runnable {
    private Resource resource;
    public SubThread(Resource resource) { this.resource = resource; }
    @Override
    public void run() {
        for (int x = 0; x < 50; x++) {
            try { resource.sub(); } catch (Exception e) { e.printStackTrace(); }
        }
    }
}

class Resource {
    private int num = 0;
    private boolean flag = true; // true:可进行加法, false:可进行减法

    public synchronized void add() throws Exception {
        if (!this.flag) { super.wait(); } // 等待减法完成
        Thread.sleep(100); // 模拟耗时
        this.num++;
        System.out.println("加法操作-" + Thread.currentThread().getName() + "num=" + this.num);
        this.flag = false; // 切换状态
        super.notifyAll(); // 唤醒其他等待线程
    }

    public synchronized void sub() throws Exception {
        if (this.flag) { super.wait(); } // 等待加法完成
        Thread.sleep(200); // 模拟耗时
        this.num--;
        System.out.println("减法操作-" + Thread.currentThread().getName() + "num=" + this.num);
        this.flag = true;
        super.notifyAll();
    }
}

生产电脑:经典生产者-消费者模型

该场景要求“生产一台,搬走一台”,涉及资源队列为空时等待生产,或资源满时等待消费的情况。这是典型的生产者-消费者(Producer-Consumer)设计模式。

解决方案如下:

  • 状态检查:通过 Computer computer 引用是否为 null 来判断库存状态。
  • 生产者 (make()):若 computer != null,说明已有产品未取走,生产者调用 wait() 阻塞;否则创建新对象,打印信息,并将引用置为 null(逻辑上已放入仓库等待消费,但在本简化模型中,通过 get 消费后才设为null,此处代码逻辑是:生产后notify,消费者拿到对象后消费并设为null,这里需要仔细对照代码:
    注意:下方代码中,生产者生产后将 this.computer 赋值,然后 notifyAll。消费者获取非null对象后,打印并设为 null,再 notifyAll。这种机制确保同一时间只有一个产品在流通。
public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Resource res = new Resource();
        new Thread(new Producer(res)).start();
        new Thread(new Consumer(res)).start();
    }
}

class Producer implements Runnable {
    private Resource resource;
    public Producer(Resource resource) { this.resource = resource; }
    @Override
    public void run() {
        for (int x = 0; x < 50; x++) { this.resource.make(); }
    }
}

class Consumer implements Runnable {
    private Resource resource;
    public Consumer(Resource resource) { this.resource = resource; }
    @Override
    public void run() {
        for (int x = 0; x < 50; x++) { this.resource.get(); }
    }
}

class Resource {
    private Computer computer;
    public synchronized void make() {
        if (this.computer != null) { // 还有存货,等待消费
            try { super.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
        }
        try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); }
        this.computer = new Computer("小米电脑", 1.1);
        System.out.println("生产电脑" + this.computer);
        super.notifyAll();
    }
    public synchronized void get() {
        if (this.computer == null) { // 无货,等待生产
            try { super.wait(); } catch (InterruptedException e) { e.printStackTrace(); }
        }
        try { Thread.sleep(200); } catch (InterruptedException e) { e.printStackTrace(); }
        System.out.println("取走电脑" + this.computer);
        this.computer = null; // 清空货物
        super.notifyAll();
    }
}

class Computer {
    private static int count = 0;
    private String name;
    private double price;
    public Computer(String name, double price) {
        this.name = name; this.price = price; count++;
    }
    public String toString() {
        return "第" + count + "台电脑 名字:" + this.name + "、价值:" + this.price;
    }
}

竞争抢答:基于Callable的异步结果获取

当多线程操作需要返回具体结果(如谁抢答成功)时,继承 Thread 或实现 Runnable 无法直接获取返回值。此时应采用 java.util.concurrent.Callable 接口配合 FutureTask 进行处理。

在此案例中,三个线程竞争修改 flag 标志位。由于 call() 内部使用了 synchronized 块保护逻辑,确保了只有第一个到达的线程能修改 flag 为 true 并返回成功,后续线程将返回失败。

package java线程;
import java.util.concurrent.Callable;
import java.util.concurrent.FutureTask;

public class ThreadDemo {
    public static void main(String[] args) throws Exception {
        Mythread mt = new Mythread();
        // FutureTask 包装 Callable 任务,可获取执行结果
        FutureTask<String> taskA = new FutureTask<>(mt);
        FutureTask<String> taskB = new FutureTask<>(mt);
        FutureTask<String> taskC = new FutureTask<>(mt);
        
        new Thread(taskA, "竞赛者A").start();
        new Thread(taskB, "竞赛者B").start();
        new Thread(taskC, "竞赛者C").start();
        
        // 主线程阻塞直到任务执行完毕并获取返回值
        System.out.println(taskA.get());
        System.out.println(taskB.get());
        System.out.println(taskC.get());
    }
}

class Mythread implements Callable<String> {
    private boolean flag = false;

    @Override
    public String call() throws Exception {
        synchronized (this) {
            if (!this.flag) {
                this.flag = true;
                return Thread.currentThread().getName() + "抢答成功";
            } else {
                return Thread.currentThread().getName() + "抢答失败";
            }
        }
    }
}

以上就是Java多线程并发控制在不同场景下应用与实现的详细内容,更多关于Java多线程学习资料请关注本站其它相关文章!

免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。

Python 相关产品和应用

全部下载
Selenium

Selenium

4.51.0 · Windows

用于浏览器自动化和 Web 测试的 Python 工具。

scikit-learn

scikit-learn

1.9.1 · Windows

常用 Python 机器学习算法与模型评估工具库。

Black

Black

26.10.1 · Windows

强调一致性的 Python 自动代码格式化工具。

Ruff

Ruff

0.17.1 · macOS

由 Rust 编写的高速 Python 代码检查和格式化工具。

pytest

pytest

9.1.1 · Windows

简洁而强大的 Python 自动化测试框架。

FastAPI

FastAPI

0.146.0 · Windows

高性能 Python API 框架,支持类型提示和自动接口文档。

Flask

Flask

3.1.3 · Windows

轻量灵活的 Python Web 应用框架。

Django

Django

6.1.2 · Windows

成熟的 Python Web 框架,适合构建内容站点和业务系统。

Spyder

Spyder

6.1.7 · Windows

面向科学计算与数据分析的 Python 集成开发环境。

SciPy

SciPy

1.18.1 · Windows

面向科学、工程和技术计算的 Python 算法库。