博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基本线程同步(四)在同步代码中使用条件
阅读量:7041 次
发布时间:2019-06-28

本文共 3275 字,大约阅读时间需要 10 分钟。

声明:本文是《 》的第二章,作者: Javier Fernández González     译者:许巧辉 校对:方腾飞

在同步代码中使用条件

在并发编程中的一个经典问题是生产者与消费者问题,我们有一个数据缓冲区,一个或多个数据的生产者在缓冲区存储数据,而一个或多个数据的消费者,把数据从缓冲区取出。

由于缓冲区是一个共享的数据结构,我们必须采用同步机制,比如synchronized关键字来控制对它的访问。但是我们有更多的限制因素,如果缓冲区是满的,生产者不能存储数据,如果缓冲区是空的,消费者不能取出数据。

对于这些类型的情况,Java在Object对象中提供wait(),notify(),和notifyAll() 方法的实现。一个线程可以在synchronized代码块中调用wait()方法。如果在synchronized代码块外部调用wait()方法,JVM会抛出IllegalMonitorStateException异常。当线程调用wait()方法,JVM让这个线程睡眠,并且释放控制 synchronized代码块的对象,这样,虽然它正在执行但允许其他线程执行由该对象保护的其他synchronized代码块。为了唤醒线程,你必 须在由相同对象保护的synchronized代码块中调用notify()或notifyAll()方法。

在这个指南中,你将学习如何通过使用synchronized关键字和wait()和notify(),notifyAll()方法实现生产者消费者问题。

准备工作

这个指南的例子使用Eclipse IDE实现。如果你使用Eclipse或其他IDE,如NetBeans,打开它并创建一个新的Java项目。

如何做…

按以下步骤来实现的这个例子:

1.创建EventStorage类,包括一个名为maxSize,类型为int的属性和一个名为storage,类型为LinkedList<Date>的属性。

public class EventStorage {private int maxSize;private List
storage;

2.实现这个类的构造器,初始化所有属性。

public EventStorage(){maxSize=10;storage=new LinkedList<>();}

3. 实现synchronized方法set(),用来在storage上存储一个事件。首先,检查storage是否已满。如果满了,调用wait()方 法,直到storage有空的空间。在方法的尾部,我们调用notifyAll()方法来唤醒,所有在wait()方法上睡眠的线程。

public synchronized void set(){while (storage.size()==maxSize){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}storage.offer(new Date());System.out.printf("Set: %d",storage.size());notifyAll();}

4. 实现synchronized方法get(),用来在storage上获取一个事件。首先,检查storage是否有事件。如果没有,调用wait()方 法直到,storage有一些事件,在方法的尾部,我们调用notifyAll()方法来唤醒,所有在wait()方法上睡眠的线程。

public synchronized void get(){while (storage.size()==0){try {wait();} catch (InterruptedException e) {e.printStackTrace();}}System.out.printf("Get: %d: %s",storage.size(),((LinkedList
)storage).poll());notifyAll();}

5.创建Producer类,并指定它实现Runnable接口,它将实现这个示例的生产者。

public class Producer implements Runnable {

6.声明一个EventStore对象,并实现(Producer类)构造器,初始化该对象。

private EventStorage storage;public Producer(EventStorage storage){this.storage=storage;}

7.实现run()方法,该方法调用EventStorage对象的set()方法100次。

@Overridepublic void run() {for (int i=0; i<100; i++){storage.set();}}

8.创建Consumer类,并指定它实现Runnable接口,它将实现这个示例的消费者。

public class Consumer implements Runnable {

9.声明一个EventStore对象,并实现(Consumer类)构造器,初始化该对象。

private EventStorage storage;public Consumer(EventStorage storage){this.storage=storage;}

10.实现run()方法,该方法调用EventStorage对象的get()方法100次。

@Overridepublic void run() {for (int i=0; i<100; i++){storage.get();}}

11.通过创建类名为Main,且包括main()方法来实现这个示例的主类。

public class Main {public static void main(String[] args) {

12.创建一个EventStorage对象。

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()方法检查storage属性是否有空闲空间。如果它满了,调用wait()方法等 待有空闲的空间。当其他线程调用notifyAll()方法,这个线程将被唤醒并且再次检查这个条件。这个notifyAll()方法并不保证线程会醒 来。这个过程是重复,直到storage有空闲空间,然后它可以生成一个新的事件并存储它。

get()方法的行为是相似的。首先,它检查storage是否有事件。如果EventStorage类是空的,调用wait()方法等待事件。当其他线程调用notifyAll()方法,这个线程将被唤醒并且再次检查这个条件直到storage有一些事件。

注释:在while循环中,你必须保持检查条件和调用wait()方法。你不能继续执行,直到这个条件为true。

不止这些…

synchronize关键字还有其他重要用法,请见其他指南中解释这个关键字使用的参见部分。

参见

在第2章,基本线程同步中在同步类中安排独立属性的食谱。

文章转自 

转载地址:http://zxaal.baihongyu.com/

你可能感兴趣的文章
IOS bug之Code Sign error:Provisioning profile
查看>>
Win10系统菜单打不开问题的解决,难道是Win10的一个Bug ?
查看>>
Xcode连接git@osc
查看>>
【Oracle】Current online Redo 和 Undo 损坏的处理方法
查看>>
TLTagsControl
查看>>
Linux内核中SPI总线驱动分析
查看>>
Java8中CAS的增强
查看>>
C语言之将无符号字符型转化为ascii码值
查看>>
maven2完全使用手册
查看>>
SQL应用与开发:(一)导论和环境
查看>>
简单封装quartz实现任务调度的配置和管理
查看>>
Android Matrix详解
查看>>
JVM 堆栈区域数据存放流程
查看>>
【MyBatis框架】配置文件-resultMap总结
查看>>
JSP生成验证码
查看>>
浏览器的窗口位置和大小
查看>>
Path实现常见toolbar点击弹出菜单效果
查看>>
AAC架构系列三(生命周期感知组件)
查看>>
Webpack 中 css import 使用 alias 相对路径
查看>>
vue router-view 切换路由默认不触发钩子函数
查看>>