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 只适用于变量的读写,而不适用于需要组合操作的情况,这可能会引发竞态条件。
相关推荐
看到我,请让我去学习12 分钟前
Qt— 布局综合项目(Splitter,Stacked,Dock)
开发语言·qt
GUET_一路向前25 分钟前
【C语言防御性编程】if条件常量在前,变量在后
c语言·开发语言·if-else·防御性编程
曳渔26 分钟前
UDP/TCP套接字编程简单实战指南
java·开发语言·网络·网络协议·tcp/ip·udp
三千道应用题43 分钟前
WPF&C#超市管理系统(6)订单详情、顾客注册、商品销售排行查询和库存提示、LiveChat报表
开发语言·c#·wpf
hqxstudying1 小时前
JAVA项目中邮件发送功能
java·开发语言·python·邮件
咪咪渝粮1 小时前
JavaScript 中constructor 属性的指向异常问题
开发语言·javascript
最初的↘那颗心1 小时前
Java HashMap深度解析:原理、实现与最佳实践
java·开发语言·面试·hashmap·八股文
小兔兔吃萝卜1 小时前
Spring 创建 Bean 的 8 种主要方式
java·后端·spring
亲爱的马哥1 小时前
重磅更新 | 填鸭表单TDuckX2.9发布!
java
Java中文社群2 小时前
26届双非上岸记!快手之战~
java·后端·面试