同步程序中使用状态

本文深入探讨了Java9并发编程中的生产者-消费者问题解决方案,利用synchronized关键字与wait(), notify()方法实现线程间的同步。通过具体实例,详细介绍了如何在EventStorage类中使用这些技术来控制生产者与消费者线程对共享资源的访问。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Java 9并发编程指南 目录

区里取出数据。

因为缓存区是共享的数据结构,就必须使用一种同步机制来控制访问,例如synchronized关键字,但是在这个问题中有更多的限制。如果缓存区已满,生产者将无法保存数据,同时如果缓存区已空,消费者也无法取走数据。

为了应对这些情况,Java提供了在Object类中实现的wait()、notify()、和notifyAll()方法。线程能够在synchronized代码块中调用wait()方法,如果它在synchronized代码块之外调用wait()方法的话,Java虚拟机抛出IllegalMonitorStateException异常。当线程调用wait()方法时,Java虚拟机让线程休眠,释放控制正在执行的synchronized代码块的对象,允许其它线程来执行剩下的被这个对象保护的sychronized代码块。想要唤醒线程,必须在被相同对象保护的代码块中调用notify()或者notifyAll()方法。

在本节中,学习如何使用synchronized关键字和wait()、notify()\notifyAll()方法来实现生产者-消费者问题。

准备工作

本范例通过Eclipse开发工具实现。如果使用诸如NetBeans的开发工具,打开并创建一个新的Java项目。

实现过程

通过如下步骤完成范例:

  1. 创建名为EventStorage的类,定义两个变量,分别是整型的maxSize和List类型的storage:

    public class EventStorage {
    	
    	private int maxSize;
    	private Queue<Date> storage;
    
  2. 实现类的构造函数,初始化类变量:

    	public EventStorage() {
    		maxSize = 10;
    		storage = new LinkedList<>();
    	}
    
  3. 实现synchronized方法set(),存储一个事件到变量storage中。首先,判断变量是否为空,如果不为空,就调用wait()方法,直到storatge有空间。在方法结尾,调用notify()方法唤醒所有在wait()方法中休眠的线程。在本范例中,忽略InterruptedException异常。但在真正的实现过程中,必须要考虑如何处理异常,可以将应用中的异常重新抛出或者转移成其它类型的异常:

    	public synchronized void set(){
    		while (storage.size() == maxSize){
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		storage.offer(new Date());
    		System.out.printf("Set: %d\n", storage.size());
    		notify();
    	}
    
  4. 实现synchronized方法get(),因为存储目的获得一个事件。首先,判断变量是否包含事件。如果没有事件,调用wait()直到变量中保存了一些事件。在方法结尾,调用notify()方法唤醒所有在wait()方法中休眠的线程。在本范例中,忽略InterruptedException异常。但在真正的实现过程中,必须要考虑如何处理异常,可以将应用中的异常重新抛出或者转移成其它类型的异常:

    	public synchronized void get(){
    		while(storage.size() == 0){
    			try {
    				wait();
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    		}
    		String element = storage.poll().toString();
    		System.out.printf("Get: %d: %s\n",storage.size(), element);
    		notify();
    	}
    
  5. 创建名为Producer的类并且指定其实现Runnable接口,此类将实现范例中的生产者:

    public class Producer implements Runnable{
    
  6. 定义一个EventStore对象,实现类的构造函数,并初始化对象:

    	private EventStorage storage;
    	
    	public Producer(EventStorage storage) {
    		this.storage = storage;
    	}
    
  7. 实现run()方法,调用EventStorage对象中的set()方法100次:

    	@Override
    	public void run() {
    		for (int i = 0; i < 100 ; i++){
    			storage.set();
    		}
    	}
    
  8. 创建名为Consumer的类并且指定其实现Runnable接口,此类将实现范例中的消费者:

    public class Consumer implements Runnable{
    
  9. 定义一个EventStore对象,实现类的构造函数,并初始化对象:

    	private EventStorage storage;
    	
    	public Consumer(EventStorage storage){
    		this.storage = storage;
    	}
    
  10. 实现run()方法,调用EventStorage对象中的get()方法100次:

    	@Override
    	public void run() {
    		for(int i = 0 ; i < 100 ; i ++){
    			storage.get();
    		}
    	}
    
  11. 创建本范例中的主类,实现一个包含main()方法的Main类:

    public class Main {
    	public static void main(String[] args) {
    
  12. 创建EventStore对象:

    		EventStorage storage = new EventStorage();
    
  13. 创建Producer对象以及运行此对象的线程:

    		Producer producer = new Producer(storage);
    		Thread thread1 = new Thread(producer);
    
  14. 创建Consumer对象以及运行此对象的线程:

    		Consumer consumer = new Consumer(storage);
    		Thread thread2 = new Thread(consumer);
    
  15. 启动两个线程:

    		thread2.start();
    		thread1.start();
    

###工作原理

本范例中的关键点是EventStorage类中的set()和get()方法。首先,set()方法检查存储属性中是否有空闲空间。如果空间已满,线程将调用wait()方法等待空闲空间。当其它线程调用notify()方法时,此线程重新启动并且再次检查是否有空闲空间。notify()方法并不保证符合状态。这个检查过程重复进行直到存储变量中有空闲空间,并且可以生成一个新的事件来存储。

get()方法性质相似。首先,检查存储属性中是否有事件。如果EventStorage类为空,线程调用wait()方法等待事件。当其它线程调用notify()方法时,此线程重新启动并且再次检查存储属性中是否有事件,直到非空为止。

在while循环中持续检查状态并且调用wait()方法,当状态为true时,循环才终止。

如果运行此范例,你会发现尽管生产者和消费者不停的保存和取出事件,存储属性始终具有包含超过10个事件的能力。

扩展学习

synchronized关键字还有其它重要用法,查看”更多关注“中的小节内容。

更多关注

  • 本章中”同步方法“小节。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值