java并发线程02

生成者消费者模型是一个经典的并发编程问题。在这个模型中,生成者(生产者)生成数据(在这个例子中是食堂的饭菜),而消费者(学生)消费这些数据。使用 List 集合替换队列,可以在一定程度上简化实现。这里给出一个简单的示例代码,使用 Java 实现学校食堂和学生消费者的模型。

java 复制代码
import java.util.ArrayList;  
import java.util.List;  

class Canteen {  
    private List<String> meals = new ArrayList<>();  
    private final int capacity = 5; // 食堂餐品的最大容量  

    public synchronized void produce(String meal) throws InterruptedException {  
        while (meals.size() == capacity) {  
            wait(); // 等待消费,满了不能再生产  
        }  
        meals.add(meal);  
        System.out.println("生成者生产了: " + meal);  
        notifyAll(); // 通知消费者可以消费了  
    }  

    public synchronized String consume() throws InterruptedException {  
        while (meals.isEmpty()) {  
            wait(); // 等待生产,空了不能消费  
        }  
        String meal = meals.remove(0);  
        System.out.println("消费者消费了: " + meal);  
        notifyAll(); // 通知生产者可以生产了  
        return meal;  
    }  
}  

class Producer implements Runnable {  
    private final Canteen canteen;  

    public Producer(Canteen canteen) {  
        this.canteen = canteen;  
    }  

    @Override  
    public void run() {  
        try {  
            String[] meals = {"米饭", "面条", "三明治", "披萨", "汉堡"};  
            for (String meal : meals) {  
                canteen.produce(meal);  
                Thread.sleep(500); // 模拟生产时间  
            }  
        } catch (InterruptedException e) {  
            Thread.currentThread().interrupt();  
        }  
    }  
}  

class Consumer implements Runnable {  
    private final Canteen canteen;  

    public Consumer(Canteen canteen) {  
        this.canteen = canteen;  
    }  

    @Override  
    public void run() {  
        try {  
            for (int i = 0; i < 5; i++) {  
                canteen.consume();  
                Thread.sleep(1000); // 模拟消费时间  
            }  
        } catch (InterruptedException e) {  
            Thread.currentThread().interrupt();  
        }  
    }  
}  

public class SchoolCanteen {  
    public static void main(String[] args) {  
        Canteen canteen = new Canteen();  
        Producer producer = new Producer(canteen);  
        Consumer consumer = new Consumer(canteen);  

        Thread producerThread = new Thread(producer);  
        Thread consumerThread = new Thread(consumer);  

        producerThread.start();  
        consumerThread.start();  
    }  
}

代码说明:

  1. Canteen 类:

    • 使用 List<String> 来存储食堂的餐品。
    • produce 方法用于生产餐品,首先检查是否达到最大容量,如果是则等待,生产后通知消费者。
    • consume 方法用于消费餐品,首先检查是否为空,如果是则等待,消费后通知生产者。
  2. Producer 类:

    • 代表生产者,不断生产预设的餐品,模拟生产时间。
  3. Consumer 类:

    • 代表消费者,模拟消费餐品,消费完后等待一段时间。
  4. SchoolCanteen 类:

    • 创建 Canteen, ProducerConsumer 实例,并启动两个线程进行生产和消费。

这个代码示例展示了如何使用 List 集合实现生成者消费者模型,同时也使用 Java 的多线程功能来模拟并发的生产和消费过程

volitle 关键字作用:

在 Java 中,volatile 关键字是一个关键字,主要用于保证多线程环境下的共享变量的可见性和禁止指令重排。以下是 volatile 关键字的主要作用:

  1. 可见性

    • 当一个线程修改了一个 volatile 变量,其他线程立即能够看到这个变量的最新值。这是因为 volatile 变量直接从主内存中读取,而不是从线程的工作内存中读取。
  2. 禁止指令重排

    • 对于 volatile 变量,编译器和运行时不会对该变量的读写进行指令重排,这保证了对 volatile 变量的操作顺序性。这意味着在该变量前面的所有操作在其后面操作之前完成。虽然这并不意味着加锁(synchronized),但在某些情况下可以用来替代加锁,提高性能。

使用场景:

volatile 关键字一般适用于以下场景:

  • 状态标志:当一个线程需要停止或中断另一个线程时,常使用 volatile 变量作为状态标志。
  • 简单的共享数据:在没有复杂同步操作的情况下,例如计数器、一致性标志等。

例子:

java 复制代码
class VolatileExample {  
    private volatile boolean running = true;  

    public void run() {  
        while (running) {  
            // 执行某些操作  
        }  
        System.out.println("线程停止运行。");  
    }  

    public void stop() {  
        running = false; // 修改 volatile 变量  
    }  
    
    public static void main(String[] args) throws InterruptedException {  
        VolatileExample example = new VolatileExample();  
        Thread thread = new Thread(example::run);  
        thread.start();  

        Thread.sleep(1000); // 主线程等待一秒  
        example.stop(); // 停止线程  
        thread.join(); // 等待线程结束  
    }  
}

在这个例子中,running 是一个 volatile 变量。当 stop 方法被调用时,running 变量被设置为 false,其他线程(例如 run 方法中的线程)能够立即看到这个变化,并且停止运行。

注意事项:

  • volatile 不能替代锁。如果多个线程需要对共享变量进行复杂的操作(如递增、减少等),则应该使用 synchronizedLock
  • volatile 只适用于变量的读写,而不适用于需要组合操作的情况,这可能会引发竞态条件。
相关推荐
程序伍六七2 分钟前
day16
开发语言·c++
wkj0017 分钟前
php操作redis
开发语言·redis·php
武子康9 分钟前
大数据-230 离线数仓 - ODS层的构建 Hive处理 UDF 与 SerDe 处理 与 当前总结
java·大数据·数据仓库·hive·hadoop·sql·hdfs
武子康11 分钟前
大数据-231 离线数仓 - DWS 层、ADS 层的创建 Hive 执行脚本
java·大数据·数据仓库·hive·hadoop·mysql
极客代码12 分钟前
【Python TensorFlow】进阶指南(续篇三)
开发语言·人工智能·python·深度学习·tensorflow
苏-言17 分钟前
Spring IOC实战指南:从零到一的构建过程
java·数据库·spring
土豆湿17 分钟前
拥抱极简主义前端开发:NoCss.js 引领无 CSS 编程潮流
开发语言·javascript·css
界面开发小八哥24 分钟前
更高效的Java 23开发,IntelliJ IDEA助力全面升级
java·开发语言·ide·intellij-idea·开发工具
草莓base38 分钟前
【手写一个spring】spring源码的简单实现--容器启动
java·后端·spring
Allen Bright1 小时前
maven概述
java·maven