生产者消费者问题(英语:Producer-consumer problem),也称有限缓冲问题(英语:Bounded-buffer problem),是一个多线程同步问题的经典案例。该问题描述了两个共享固定大小缓冲区的线程——即所谓的“生产者”和“消费者”——在实际运行时会发生的问题。生产者的主要做用是生成必定量的数据放到缓冲区中,而后重复此过程。与此同时,消费者也在缓冲区消耗这些数据。该问题的关键就是要保证生产者不会在缓冲区满时加入数据,消费者也不会在缓冲区中空时消耗数据。java
要解决该问题,就必须让生产者在缓冲区满时休眠(要么干脆就放弃数据),等到下次消费者消耗缓冲区中的数据的时候,生产者才能被唤醒,开始往缓冲区添加数据。一样,也可让消费者在缓冲区空时进入休眠,等到生产者往缓冲区添加数据以后,再唤醒消费者。一般采用进程间通讯的方法解决该问题,经常使用的方法有信号灯法等。若是解决方法不够完善,则容易出现死锁的状况。出现死锁时,两个线程都会陷入休眠,等待对方唤醒本身。该问题也能被推广到多个生产者和消费者的情形。linux
c++
JAVA多线程同步主要依赖于若干方法和关键字数组
1 wait方法:安全
该方法属于Object的方法,wait方法的做用是使得当前调用wait方法所在部分(代码块)的线程中止执行,并释放当前得到的调用wait所在的代码块的锁,并在其余线程调用notify或者notifyAll方法时恢复到竞争锁状态(一旦得到锁就恢复执行)。网络
调用wait方法须要注意几点:多线程
第一点:wait被调用的时候必须在拥有锁(即synchronized修饰的)的代码块中。app
第二点:恢复执行后,从wait的下一条语句开始执行,于是wait方法老是应当在while循环中调用,以避免出现恢复执行后继续执行的条件不知足却继续执行的状况。函数
第三点:若wait方法参数中带时间,则除了notify和notifyAll被调用能激活处于wait状态(等待状态)的线程进入锁竞争外,在其余线程中interrupt它或者参数时间到了以后,该线程也将被激活到竞争状态。atom
第四点:wait方法被调用的线程必须得到以前执行到wait时释放掉的锁从新得到才可以恢复执行。
2 notify方法和notifyAll方法:
notify方法通知调用了wait方法,可是还没有激活的一个线程进入线程调度队列(即进入锁竞争),注意不是当即执行。而且具体是哪个线程不能保证。另一点就是被唤醒的这个线程必定是在等待wait所释放的锁。
notifyAll方法则唤醒全部调用了wait方法,还没有激活的进程进入竞争队列。
3 synchronized关键字:
第一点:synchronized用来标识一个普通方法时,表示一个线程要执行该方法,必须取得该方法所在的对象的锁。
第二点:synchronized用来标识一个静态方法时,表示一个线程要执行该方法,必须得到该方法所在的类的类锁。
第三点:synchronized修饰一个代码块。相似这样:synchronized(obj) { //code.... }。表示一个线程要执行该代码块,必须得到obj的锁。这样作的目的是减少锁的粒度,保证当不一样块所需的锁不冲突时不用对整个对象加锁。利用零长度的byte数组对象作obj很是经济。
4 atomic action(原子操做):
在JAVA中,如下两点操做是原子操做。可是c和c++中并不如此。
第一点:对引用变量和除了long和double以外的原始数据类型变量进行读写。
第二点:对全部声明为volatile的变量(包括long和double)的读写。
另外:在java.util.concurrent和java.util.concurrent.atomic包中提供了一些不依赖于同步机制的线程安全的类和方法。
附录:
进程间通讯(IPC,Inter-Process Communication),指至少两个进程或线程间传送数据或信号的一些技术或方法。线程是计算机系统分配资源的最小单位。每一个进程都有本身的一部分独立的系统资源,彼此是隔离的。为了能使不一样的进程互相访问资源并进行协调工做,才有了进程间通讯。这些进程能够运行在同一计算机上或网络链接的不一样计算机上。
进程间通讯技术包括消息传递、同步、共享内存和远程过程调用。IPC是一种标准的Unix通讯机制。
主要的IPC方法有
(1)管道(Pipe):管道可用于具备亲缘关系进程间的通讯,容许一个进程和另外一个与它有共同祖先的进程之间进行通讯。
(2)命名管道(named pipe):命名管道克服了管道没有名字的限制,所以,除具备管道所具备的功能外,它还容许无亲缘关系进程间的通讯。命名管道在文件系统中有对应的文件名。命名管道经过命令mkfifo或系统调用mkfifo来建立。
(3)信号(Signal):信号是比较复杂的通讯方式,用于通知接受进程有某种事件发生,除了用于进程间通讯外,进程还能够发送信号给进程自己;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又可以统一对外接口,用sigaction函数从新实现了signal函数)。
(4)消息(Message)队列:消息队列是消息的连接表,包括Posix消息队列system V消息队列。有足够权限的进程能够向队列中添加消息,被赋予读权限的进程则能够读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺
(5)共享内存:使得多个进程能够访问同一块内存空间,是最快的可用IPC形式。是针对其余通讯机制运行效率较低而设计的。每每与其它通讯机制,如信号量结合使用,来达到进程间的同步及互斥。
(6)内存映射(mapped memory):内存映射容许任何多个进程间通讯,每个使用该机制的进程经过把一个共享的文件映射到本身的进程地址空间来实现它。
(7)信号量(semaphore):主要做为进程间以及同一进程不一样线程之间的同步手段。
(8)套接口(Socket):更为通常的进程间通讯机制,可用于不一样机器之间的进程间通讯。起初是由Unix系统的BSD分支开发出来的,但如今通常能够移植到其它类Unix系统上:Linux和System V的变种都支持套接字。