多线程同步之生产者---消费者模型

多线程同步之生产者---消费者模型

线程同步是个老生常谈的问题了,在这里我将通过一个Java多线程程序,来说明控制相互交互的线程之间的运行进度,使程序运行总是既高效又稳定。这个多线程程序将采用生产者---消费者模型,来说明怎么样实现多线程的同步。

如果让我定义一下什么是消费者、生产者:我觉得可以把系统中使用某种资源的线程称为消费者,产生该种资源的线程称为生产者。在下面的Java的应用程序中,生产者线程向一个线程安全的堆栈缓冲区中写(PUSH)数据,消费者从该堆栈缓冲区中读(POP)数据,这样,这个程序中同时运行的两个线程共享同一个堆栈缓冲区资源。

类Producer是生产者模型,其中的run方法中定义了生产者线程所做的操作,循环调用push()方法,将生产的100个字母送入堆栈中,每次执行完push操作后,调用sleep方法睡眠一段随机时间。

类Consumer是消费者模型,循环调用pop方法,从堆栈取出一个字母,一共取100次,每次执行完push操作后,调用sleep方法睡眠一段随机时间。

先看下同步堆栈类的源码:

有了同步堆栈类,接着看看我们的生产者类的源码:

接着看下消费者类的源码:

最后让我们来看看主程序main所在类的源码:

在本例中,使用了一个生产者线程和一个消费者线程,当生产者线程往堆栈中添加字符时,如果堆栈已满,通过调用this.wait方法,(这里,this就是互斥锁)把自己加入到互斥锁对象(SyncStack)的锁等待队列中,如果该堆栈不满,则该生产者线程加入到互斥锁对象(SyncStack)的锁申请队列中,并且很快就被JVM取出来执行。当生产者线程在执行添加字符操作的时候,消费者是不能从中取出字符的,只能在等待队列中等待,当生产者添加完字符的时候,使用this.notify()(这里,this就是互斥锁)把等待队列中的第一个消费者唤醒,把它加入到锁申请队列中,很快该消费者线程就会获得CPU时间。此时的生产者线程已经无法再次添加字符,因为消费者线程正在synchronized代码块中运行,JVM把生产者线程加入锁等待队列中。当消费者线程从堆栈中取完字符后,再使用this.notify()方法把生产者从等待进程中唤醒,添加字符,如此循环往复,直到生产者线程和消费者线程都运行结束。

下面是该程序的某次运行结果:

栈被创建
Produced:Z
Consumed:Z
栈空了!
Consumed:G
Produced:G
栈空了!
Consumed:J
Produced:J
栈空了!
Consumed:O
Produced:O
栈空了!
Consumed:A
Produced:A
Produced:T
Consumed:T
Produced:M
Produced:H
Consumed:H
Consumed:M
Produced:D
Produced:B
Consumed:B
Produced:W
Consumed:W
Produced:C
Produced:P
Consumed:P
Consumed:C
Produced:P
Consumed:P
Produced:N
Consumed:N
Consumed:D
Produced:V
Consumed:V
栈空了!
Consumed:V
Produced:V
Produced:Z
Consumed:Z
Produced:J
Consumed:J
Produced:O
Consumed:O
栈空了!
Consumed:B
Produced:B
Produced:N
Consumed:N
Produced:H
Consumed:H
栈空了!
Consumed:G
Produced:G
栈空了!
Consumed:F
Produced:F
Produced:W
Consumed:W
Produced:F
Produced:W
Consumed:W
Consumed:F
Produced:X
Consumed:X
栈空了!
Consumed:D
Produced:D
栈空了!
Consumed:H
Produced:H
栈空了!
Consumed:Q
Produced:Q
Produced:A
Produced:X
Produced:B
Produced:B
Consumed:B
Consumed:B
Consumed:X
Produced:M
Produced:J
Consumed:J
Consumed:M
Produced:G
Consumed:G
Produced:I
Consumed:I
Consumed:A
Produced:B
Produced:Q
Consumed:Q
Consumed:B
Produced:D
Consumed:D
栈空了!
Consumed:X
Produced:X
栈空了!
Consumed:B
Produced:B
Produced:S
Consumed:S
Produced:N
Produced:O
Produced:L
Consumed:L
Consumed:O
Produced:B
Consumed:B
Produced:N
Produced:G
Consumed:G
Consumed:N
Produced:M
Produced:R
Consumed:R
Consumed:M
Consumed:N
Produced:O
Consumed:O
Produced:A
Consumed:A
Produced:N
Produced:D
Consumed:D
Produced:P
Consumed:P
Produced:W
Produced:Z
Produced:M
Consumed:M
Consumed:Z
Produced:C
Consumed:C
Produced:H
Consumed:H
Consumed:W
Produced:X
Produced:J
Consumed:J
Produced:P
Consumed:P
Produced:S
Consumed:S
Produced:Y
Consumed:Y
Produced:O
Consumed:O
Produced:U
Produced:H
Consumed:H
Consumed:U
Produced:C
Consumed:C
Consumed:X
Produced:K
Produced:F
Produced:S
Consumed:S
Consumed:F
Consumed:K
Produced:K
Consumed:K
Consumed:N
Produced:L
Produced:M
Consumed:M
Produced:I
Consumed:I
Produced:Y
Produced:L
Consumed:L
Consumed:Y
Produced:N
Produced:W
Consumed:W
Consumed:N
Produced:J
Consumed:J
Produced:L
Consumed:L
Produced:Y
Produced:D
Consumed:D
Produced:D
Consumed:D
Produced:R
Produced:J
Consumed:J
Produced:G
Consumed:G
Produced:W
Produced:T
栈满了!
Consumed:T
Produced:Y
Consumed:Y
Produced:E
栈满了!
Produced:O
Consumed:E
Consumed:O
Produced:J
Consumed:J
Produced:M
栈满了!
Produced:I
Consumed:M
Consumed:I
Consumed:W
Consumed:R
Consumed:Y
Consumed:L

下面是我对wait(),notify()和notifyAll()等几个同步方法的一个总节:
(1)wait,notify,notifyAll必须在已经持有锁的情况下执行,所以它们只能出现在synchronized作用范围内,也就是出现在用synchronized修饰的方法或代码块中
(2)wait的作用:释放已持有的锁,把当前的线程加入到锁等待队列中
(3)notify的作用:唤醒等待队列中的第一个线程并把它移入锁申请队列中
(4)notifyAll的作用:唤醒等待队列中的所有的线程并把它们移入锁申请队列中

by Loomman, QQ:28077188, MSN: Loomman@hotmail.com QQ裙:30515563 ☆程序天堂☆ 请尊重作者原创,转载注明来自裂帛一剑博客,谢谢合作。 

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值