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 只适用于变量的读写,而不适用于需要组合操作的情况,这可能会引发竞态条件。
相关推荐
远望樱花兔13 分钟前
【d54_2】【Java】【力扣】142.环形链表
java·leetcode·链表
IT学长编程14 分钟前
计算机毕业设计 助农产品采购平台的设计与实现 Java实战项目 附源码+文档+视频讲解
java·spring boot·毕业设计·课程设计·毕业论文·计算机毕业设计选题·助农产品采购平台
潘潘潘潘潘潘潘潘潘潘潘潘14 分钟前
【C++】类和对象(下)
c语言·开发语言·c++·学习方法
2401_8572979119 分钟前
2025校招内推-招联金融
java·前端·算法·金融·求职招聘
编啊编程啊程20 分钟前
一文上手Kafka【下】
java·分布式·中间件·kafka
写bug如流水23 分钟前
【Python】Python闭包的妙用与注意事项
开发语言·python·spring
誓则盟约36 分钟前
基于Spring框架的分层解耦详解
java·后端·spring
做人不要太理性39 分钟前
C++:模拟实现string
开发语言·c++
Mr_Xuhhh40 分钟前
vector
c语言·开发语言·数据结构·算法·链表·visualstudio
鸽芷咕41 分钟前
【C++报错已解决】std::ios_base::floatfield
开发语言·c++·ios