版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、生产者消费者生产者-消费者(producer-consumer)问题,也 称作有界缓冲区(bounded-buffer)问题,两个 进程共享一个公共的固定大小的缓冲区。其中一 个是生产者,用于将消息放入缓冲区;另外一个 是消费者,用于从缓冲区中取出消息。问题出现 在当缓冲区已经满了,而此时生产者还想向其中 放入一个新的数据项的情形,其解决方法是让生 产者此时进行休眠,等待消费者从缓冲区中取走 了一个或者多个数据后再去唤醒它。同样地,当 缓冲区已经空了,而消费者还想去取消息,此时 也可以让消费者进行休眠,等待生产者放入一个 或者多个数据时再唤醒它。听起来好像蛮对的,无懈可击似的,但其实在实 现时
2、会有一个竞争条件存在的。为了跟踪缓冲区 中的消息数目,需要一个变量counto如果缓冲 区最多存放N个消息,则生产者的代码会首先 检查count是否达到N,如果是,则生产者休 眠;否则,生产者向缓冲区中放入一个消息,并 增加count的值。消费者的代码也与此类似,首先检测count是否为0,如果是,则休眠;否则,从缓冲区中取 出消息并递减count的值。同时,每个进程也 需要检查是否需要唤醒另一个进程。代码可能如 下:/跟踪缓冲/缓冲/缓冲区大小 ttdefine N 100 int count = 0; 区的记录数 /*生产者进程*/ void procedure(void) (int it
3、em; 区中的数据项while(true)/ 无限循环( item = produce_item(); /产生下一个数据项if (count = N)/如果缓冲区满了,进行休眠(sleep ();)insert_item(item);/将新数据项放入缓冲区count = count +1;/计数器加1if (count =1)/表明插入之前为空,(/消费者等待wakeup(consumer);/唤醒消费者)/*消费者进程*/void consumer(void)int item;/ 缓冲区中的数据项while(true)/ 无限循环(if (count =0)/如果缓冲区为空,进入休眠( sl
4、eep ();item = remove_item(); /从缓冲区中取出一个数据项count = count -1;/计数器减1if (count = N -1) /缓冲区有空槽( /唤醒生产者wakeup(producer);consume_item (item);/打印出数据项 看上去很美,哪里出了问题,这里对count的 访问是有可能出现竞争条件的:缓冲区为空,消 费者刚刚读取count的值为0,而此时调度程 序决定暂停消费者并启动执行生产者。生产者向 缓冲区中加入一个数据项,count加lo现在 count的值变成了 1.它推断刚才count为0, 所以此时消费者一定在休眠,于是生产
5、者开始调 用wakeup (consumer)来唤醒消费者。但是,此 时消费者在逻辑上并没有休眠,所以wakeup信 号就丢失了。当消费者下次运行时,它将测试先 前读到的count值,发现为0 (注意,其实这个时刻count已经为1 了),于是开始休眠(逻 辑上)。而生产者下次运行的时候,count会继 会填满缓冲区的,然后生产者也休眠,这样两个 进程就都永远的休眠下去了。续递增,并且不会唤醒consumer了,所以迟早b使用信号量解决生产者-消费者问题首先了解一下信号量吧,信号量是E. W. Dijkstra在1965年提出的一种方法,它 是使用一个整型变量来累计唤醒的次数,供以后 使用。在
6、他的建议中,引入了一个新的变量类型, 称为信号量(semaphore), 一个信号量的取值可 以为0 (表示没有保存下来的唤醒操作)或者为 正值(表示有一个或多个唤醒操作)。并且设立了两种操作:down和up (分别为一般 化后的sleep和wakeup,其实也是一般教科书 上说的P/V向量)。对一个信号量执行down操 作,表示检查其值是否大于0,如果该值大于0, 则将其值减1 (即用掉一个保存的唤醒信号)并 继续;如果为0,则进程休眠,而且此时down操 作并未结束。另外,就是检查数值,修改变量值 以及可能发生的休眠操作都作为单一的,不可分 割的原子操作来完成。下面开始考虑用信号量来解决生
7、产者-消费者问 题了,不过在此之前,再次分析一下这个问题的本质会更清晰点:问题的实质在于发给一个(尚) 未休眠进程(如上的消费者进程在只判断了 count二二0后即被调度出来,还未休眠)的 wakeup信号丢失(如上的生产者进程在判断了 count = 1后以为消费者进程休眠,而唤醒它)To如果它没有丢失,则一切都会很好。100/SdefineN/缓冲区中的槽数目 typedef int semaphore;信号量一般被定义为特殊的整型数据semaphore mutex = 1; 制对临界区的访问 semaphore empty = N; 数缓冲区中的空槽数目 semaphore full/控
8、/计二 0;/计数缓冲区中的满槽数目 /*生产者进程*/void proceducer(void)int item;while (1)item = procedure_item();/生成数据down(&empty);/将空槽数目减down(ftmutex);/进入临界区insert_item(item);/将新数据放入缓冲区up(ftmutex);/离开临界区up(ftfull);/将满槽的数目加1/*消费者进程*/ void consumer(voi)int item;while (1)down (&full);/将满槽数目减down(&mutex);/进入临界区i
9、tem = remove_item();/从缓冲区中取出数据项up(ftmutex);/离开临界区up (ftempty);/将空槽数目加1consumer_i t em (i t em);/处理数据项该解决方案使用了三个信号量:一个为full, 用来记录充满的缓冲槽的数目,一个为empty, 记录空的缓冲槽总数,一个为mutex,用来确保 生产者和消费者不会同时访问缓冲区。mutex的初始值为1,供两个或者多个进程使用的信号1=1用I,保证同一个时刻只有一个进程可以进入临界区,称为二元信号量(binary semaphore)o如 果每一个进程在进入临界区前都执行一个 down(.),在刚刚
10、退出临界区时执行一个 up(.),就能够实现互斥。另外,通常是将down和up操作作为系统调用 来实现,而且OS只需要在执行以下操作时暂时 禁止全部中断:测试信号量,更新信号量以及在 需要时使某个进程休眠。这里使用了三个信号量,但是它们的目的却不相 同,其中 full和 empty用来同步 (synchronization),而 mutex 用来实现互斥。2,使用消息传递解决生产者-消费者问题这种IPC方式使用两条原语send和 receive,也是系统调用。如:send(dest, &msg)/将消息msg发送到目标(进程)dest中receive (src, &msg) /
11、 接收由 src 过 来的msg,如果没有消息可用,则可能阻塞接收 者 消息传递系统会面临位于网络中不同机器上的 通信进程的情形,所以会更加的复杂。如:消息 可能被网络丢失,一般使用确认(ACK)消息。如果发送方在一定的时间段内没有收到确认消 息,则重发消息。如果消息本身被正确接收,但是返回的ACK消 息丢失,发送方则重发消息,这样接收方就会收 到两份同样的消息。一般使用在每条原始消息的 头部嵌入一个连续的序号来解决这个问题。另外,消息传递系统还需要解决进程命名的问 题,在send和receive系统调用中指定的进 程必须没有二义性的。还有其他的一些问题,如 性能问题,身份认证等等,不过那个就
12、会扯多了, 还是看看如果解决这个生产者-消费者的问题 吧:ttdefineN/缓冲区中的槽数目100/*生产者进程*/ void proceducer(void) (int item;msg;message/消息缓冲区while (1)item = proc edur e_item ();/生成数据receive(consumer, &msg);/等待消费者发送空的缓冲区build_msg(&msg,item);/创建待发送消息send (consumer,&msg);/发送数据项给消费者/*消费者进程*/ void consumer(voi) (int item, i
13、;message msg;&msg);for (i=0; i<N; i+) send(producer, /发送给生产者N个空缓冲区while(l)receive (producer,&msg);/接收包含数据项的消息item = extract_item(&msg);/解析消息,并组装成数据项send (proceduer, &msg);/然后又将空缓冲区发送回生产者consumer_item(item);/处理数据项在这个解决方案中,共使用了 N条消息,有点类似于上一个的共享内存缓冲区的N个槽,消 费者进程这边首先通过一个for循环将N条空 消息发送给
14、生产者。当生产者向消费者传递一个 数据项时,是通过取走每一条接收到的空消息, 然后送回填充了内容的消息给消费者的。通过这 种方式,整个消息传递系统中的总的消息数(包 括空的消息+存了数据项的消息=N)是不变 的。如果运行过程中,生产者进程的速度比消费者 快,则所有的消息最终都会塞满,然后生产者进 程就会等待消费者(即使调用procedure也是 阻塞在receive处),直到消费者返回一条空的 消息;反之亦然。F面再来看一下消息传递方式的两种变体。一种是:为每一个进程分配一个唯一的地址,让消息 按照这个进程的地址进行编址。也就是send和receive调用的第一个参数指定为具体的进程 地址。另
15、一种是:引入信箱(mailbox,现在正 在做的一个项目,就是这种方式),可以信箱就 像一个盒子,里面装了很多的信件,这个信件就 是我们要传递的消息,当然信箱是有容量限制的(现在yahoo好像推出无限容量的,呵呵)。当 使用信箱时,send和receive系统调用中的地 址参数就是信箱的地址,而不是进程的地址。当 一个进程尝试向一个容量爆满的信箱发送消息 时,它将会被挂起,直到信箱中有消息被取走。多线程一,线程的一些基本知识。进程与线程所有的操作系统都支持同时运行多个任务,一 个任务通常就是一个程序,每个运行中就是一个 进程,当一个程序运行时,内部可能包含了多个 顺序执行流,每个顺序执行流就是
16、一个线程。进程(process)当一个程序进入内存运行即变成一个进程,进 程处于运行过程中的程序,并且具有一定的独立 功能,进程是系统进行资源分配和调用的独立单 位,进程切换开销大。多进程在操作系统中,能同时运行多个任务程序。进程包含三大特征1,独立性:进程是系统中独立存在的实体,它可以拥有自 己独立的资源,每一个进程都拥有自己私有的地 址空间,没有经过进程本身允许的情况下,一个 用户不可以直接访问其他进程的地址空间。2,动态性:进程与程序的区别在于程序只是个静态的指定 集合,而进程是一个正在系统中活动的指定集 合,在进程中加入了时间的概念,进程具有自己 的生命周期和各种不同的状态,这些状态在
17、程序 中是不具备的。3,并发性:多个线程可以在单个处理器是并发执行多个进程,进程之间,不会互相影响。注:对于一个CPU而言,它在某个时间点上只能执 行一个程序,就是说只能运行一个进程。CPU不 断在这些进程之间轮回切换,CPU的执行速度相 对于我们的感觉太快,我们感觉不到,所以CPU 在多个进程之间轮回执行,但我们人类感觉好像 多个进程同时执行。线程(Thread)线程被称为轻量级的进程,线程是进程的执行 单元,就像进程在操作系统中的地位一样。线程 在程序中是独立的,并发的执行的流。当进程被 初始化后,主线程就被创建了,一般的应用程序 来说,通常仅要求有一个主线程,但我们也可以 在该进程内创建
18、多条顺序执行流,这些顺序执行 流就是线程,每条线程是相互独立的。注:总而言之,一个程序运行后,至少有一个进程, 一个进程里可以包含多个线程,但至少要包含一个线程。多线程(multithreading)在同一个应用程序中,有多个顺序执行流同时 执行。线程比进程具有更高的性能,这是由于同一个 进程中的线程都有共性,多个线程将共享同一个 进程的虚拟空间,-线程共享的环境包括进程代 码段,进程的公有数据等,利用这些共享的数据, 线程很容易实现相互之间的通信。当操作系统创建一个进程时,该进程必须分配 独立的内存空间,分配大量相关资料,但创建一 个线程简单的多,因此使用多线程来实现并发比 使用多进程来实现
19、并发的性能要高得多。多线程的优点1,进程之间不能共享内存,但线程之间共享内存非常容易。2,系统创建进程需要为该进程重新分配系统资源,但创建线程代价要小的多,因此使用多线程来实现多个任务并发比多进程要效率高。3, Java语言中内置多线程功能的支持,而不是单纯的作为底层的操作系统的调动方式,从而 简化了 Java的多线程编程。二,线程的启动和创建Thread 类(Java. lang. Thread 类)在Java语言中Thread类代表线程,所有的线 程对象都必须是Thread类或其子类的实例。每 条线程的作用是完成一定的任务,实际上就是执行一段程序流,也叫一段顺序执行的代码,Java使用ru
20、n方法来封装这样一段程序流。继承Thread类创建和启动线程1,定义Thread类的子类,并重写该类的run 方法,该run方法的方法体就是代表了线程需要 完成的任务,run方法也被称为线程执行体。2,创建Thread类子类的实例,即创建了线程的对象。3,用线程对象的start方法来启动该线程。start ()方法使该线程开始执行,Java虚拟机调用该线程的 run方法。getName ()方法返回该线程的名称setName (String name)方法修改该线程的名称interrupt ();中断线程sleep(long millis);在指定的毫秒内让当前正在执行的线程休眠.1,实现Ru
21、nable接口的实现类,并重写该接口 的run方法,该run方法的方法体同样是该线程 的执行体。2,创建Runable实现类的实例,并以此实例作为Thread类的目标来创建Thread类的目标创建Thread对象,该Thread对象才是真正的线程对 象。Thread类和Runable接口创建线程的对比 继承Thread类方式的多线程的优点: 编写简单,如果访问当前线程,无需使用Thread. currentThread 方法,直接使用 this,即可获得当前线程。继承Thread类方式的多线程缺点:因为线程已经继承了 Thread类,所以不能再继 承其他的类。实现Runable接口的优点:线程
22、只是实现了 Runable接口,还可以继承其他的类,在这种方式下,可以多个线程共享同一 个目标(target)对象,所以非常适合多个线程来 处理同一份资源的情况,从而可以将CPU代码和 数据分开,形成清晰的模型,体现了面向对象的 思想。实现Runable接口的缺点:编程稍微复杂,如果需要访问当前线程,必须 使用 Thread. currentThread 方法。三,线程的生命周期当线程被创建并启动以后,它既不是一启动就 进入了执行的状态,也不是一直处于执行状态, 在线程的生命周期里,它要经过新建,就绪,运 行,阻塞,死亡五种状态。尤其是当线程启动以后,它不能一直霸占着CPU独立运行,所以CPU
23、 多条线程之间切换,于是线程状态也会多次在运 行和阻塞之间切换。新建或就绪状态启动线程使用start方法,而不是run方法, 永远不用调用线程对象的run方法,调用start 方法来启动线程系统会把该run方法当成线程 执行体来处理。但如果直接调用线程对象的run 方法,则run方法立即会被执行,而且在run方法返回之前其他线程无法并发执行,也就是说系 也是一个普通方法,而不是线程执行体。统吧线程对象当成一个普通的对象,而run方法当线程对象调用了 start方法之后,该线程处 于就绪状态,JVM会为创建方法调用栈和线程计 数器处于这个状态中的线程,并没有开始运行, 它只是表示该线程可以运行了
24、,至于该线程何时 开始运行,取决于JVM里线程调度器的调度。运行和阻塞状态如果处于就绪状态的线程获得了 CPU资源,开始执行run方法的执行体,则该线程处于运行状 态,当一条线程开始运行后,它不可能一直处于 运行状态,线程在运行的过程中,需要被中断, 目的是使其他线程获得执行的机会,当发生以下 情况下,线程将会进入阻塞状态;1,线程调用sleep方法主动放弃所占有的CPU 资源。2,线程调用一个阻塞式10方法,该方法返回之前该线程被阻塞。3,线程试图获得一个同步监视器,但该同步监视器正被其他线程所占据。4,线程在等待某个通知(ntify, ntifyAll方法 在Objiet类里)。5,程序调
25、用了线程的suspend方法,将线程挂 起暂停),不过这个方法容易导致死锁,所以程 序尽量避免该方法。线程重新进入就绪状态有以下情况:b调用sleep方法的线程经过指定的时间。2,线程调用阻塞式10方法已经返回。3,线程成功的获得了试图取得同步监视器o4,线程正在等待某个通知,其他线程发出了通知。5,处于挂起状态的线程被调用了 resume方法 恢复。注:阻塞状态也叫不可运行状态调用yield方法可以让当前运行的线程转入就 绪状态。线程会在以下三种方式之一结束线程,处于死 亡状态:1, run方法执行完成,线程正常结束。2,线程抛出一个未捕获的Exception或Erroro3,直接调用线程的
26、stop方法来结束该线程, 该方法容易导致死锁,通常不推荐用。isAlive 方法测试某条线程是否已经死亡,当线程处于就绪, 运行,阻塞三种状态时,该方法返回true,当 该线程处于创建,死亡两种状态时,该方法返回false。注:不要试图对一个已经死亡的线程调用start方 法,使它重新启动,该线程将不可再次作为线程 执行,它会抛出 IllegaThreadStateException 异常。不要让处于死亡状态的线程调用start方法, 程序只能对新建状态的线程调用start方法,对 新建状态的线程两次调用start方法也是错误 的。join()方法等待被join的线程执行完成。join (l
27、ong millis)方法等join的新车的时间最长为minllis毫秒,如 果在nd 11 i s毫秒内被join的线程还没有执行结 束,则不再等待。join (long millis , int nanos)方法等待被join的时机最长为millis毫秒加nanos 纳秒。setPriority ()和方法:来设值和取值,返回线程的优先级,其中setPriority可以是 个整数,范围是一到十之间,也可以是MAX_PRIORITY , MIN_PRIORITY , N0RM_PRI0RITY三个静态常量。getPriority ()方法返回线程的优先级。每个线程执行时,都具有 一定的优先级
28、,优先级高的线程获得较多的执行 机会,而优先级低的线程则获得较小的执行机 会。后台线程(DaemonThread)有一种线程它是在后台运行的,它的任务是为 其他线程提供服务,这种线程称为后台线程,又 称为守护线程,又称为精灵线程。JVM的垃圾回 收线程就是典型的后台线程。后台线程有个特 征:如果所有的前台线程都死亡,后台线程会自 动死亡。调用Thread对象的setDaemon(blooeanon)方法(参数传true),可将指定线程设置为后台线程,isDaemonO判断该线程是否为后台线 程,如果该线程是守护线程,则返回true;否 则返回falseosleep (long mi 11 is
29、)方法(线程睡眠)让当前正在执行的线程暂停并进入阻塞状态, 该方法受到系统计时器和线程调度器的精度和 准确度的影响。注:当前线程调用sleep方法,进入阻塞状态后, 在sleep时间段内,该线程不会获得执行的机 会,即使系统中没有其他可运行的线程,处于sleep时间段里的线程也不会运行。因此sleep 常用来暂停程序的执行,yield。方法(线程让步)yield方法和sleep有点相似的方法,让当前 正在执行的线程暂停,但它不会阻塞该线程,它 只是将该线程转入就绪状态,yield方法只是让 当前线程暂停一下,让系统的线程调度器从新调 度一次,当某个线程调用了 yield方法暂停之 后,只有优先
30、级与当前线程相同,或者优先级比 当前线程更高的就绪状态的线程才会获得执行的机会sleep方法和yield方法的区别L sleep方法暂停当前线程后,会给其他线程 执行机会,不会理会其他线程的优先级,但yield 方法只会给优先级相同或优先级更高的线程执 行机会。2, sleep方法会将线程转入阻塞状态,直到经 过阻塞时间,才会转入就绪状态,而yield方法 不会将线程转入阻塞状态,它只是强制当前线程 进入就绪状态,因此完全有可能某个线程用yield方法暂停之后立即再次获得处理器资源被执行。3, sleep方法声明抛出 InterruptedException 异常。所有调用sleep方法时,要
31、么扑捉该异常, 要么显示声明抛出该异常。而yield方法则没有 声明抛出任何异常。4, sleep方法比yield方法有更好的可移植性,通常不用依靠yield方法来控制并发线程的执 行。interrupt ()使该线程中断,如果一个线程抛出异常,可以 用interrupt在catch里中断该线程.Thread ()分配新的Thread对象。Thread(Runnable target)分配新的Thread对象。Thread(Runnable target, String name) 分配新的Thread对象。Thread (String name)static Thread currentTh
32、read()返回对当前正在执行的线程对象的引用。String getName ()返回该线程的名称。void setName(String name)改变线程名称,使之与参数name 相同。int getPriority()返回线程的优先级。void setPriority(int newPriority)更改线程的优先级。void interrupt ()中断线程。boolean isAlive ()测试线程是否处于活动状态。boolean isDaemon ()测试该线程是否为守护线程。void join()等待该线程终止。void join(long millis)等待该线程终止的时间最长为millis 毫秒。void run ()如果该线程是使用独立的 Runnable运行对象构造的,则调用该Runnable 对象的run方法;否则,该方法不执行任何操 作并返回。void setDaemon(boolean on)将该线程标记为守护线程或用户线43Eovoid start ()使该线程开始执行;Java虚拟机调 用该线程的run方法。static void yield()暂停当前正在执行的线程对象,并 执行其他线程。ThreadGroup 类T
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 职业学院船舶动力工程技术(船舶管系专业)人才培养方案
- 唇色调和剂产业运行及前景预测报告
- 升降作业平台市场发展预测和趋势分析
- 扫雪机产业规划专项研究报告
- 婴儿摇铃产品供应链分析
- 冲浪皮划艇产业规划专项研究报告
- 医用诊断试剂产业深度调研及未来发展现状趋势
- 抗疲劳地垫市场发展预测和趋势分析
- 内存扩展模块产业规划专项研究报告
- 催乳剂产业规划专项研究报告
- 军乐队乐器种类以及人员编制
- 常见皮肤病讲稿
- 高中化学选修4《化学反应原理》全册教案
- 创建学习型医院实施方案
- 大学《通用英语1》 期中测试卷试题
- 新人教选择性必修一 Unit 4:Discover Useful Structures
- 公共政策导论完整版课件全套ppt教学教程(最新)
- 《乡土中国》整本书阅读 高中语文 必修上册
- 2022年反洗钱考试题库及答案
- 2021年电力设备预防性试验规程
- 创意大自然动物世界保护野生动物动物介绍PPT模板
评论
0/150
提交评论