Dijkstra建议设两种操作:Down和Up。对一信号量执行Down操作是检查其值是否大于0;若是则将其值减1(即用掉一个保存的唤醒信号)并继续。若值为0,则进程将睡眠,而且此时Down操作并未结束。检查数值,改变数值以及可能发生的睡眠操作均作为一个单一的、不可分割的原子操作(atomic action)完成。即保证一旦一个信号量操作开始,则在操作完成或阻塞之前别的进程均不允许访问该信号量。这种原子性对于解决同步间题和避免竞争条件是非常重要的。
Up操作递增信号量的值。如果一个或多个进程在该信号量上睡眠,无法完成一个先前的Down操作、则由系统选择其中的一个(例如,随机挑选)并允许其完成它的Down操作。于是,对一个有进程在其上睡眠的信号量执行一次Up操作之后,该信号量的值仍旧是0.但在其上睡眠的进程却少了一个。递增信号量的值和唤醒一个进程同样也是不可分割的。不会有进程因执行Up而阻塞。
JDK的并发包就给我提供了一个类似的信号量类——Semaphore ,其中的acquire()和release()方法就相当于Down和Up操作,这两个方法的特殊之处在于对Semaphore对象的操作都是原子操作,由操作系统底层来支持。下面我们讲的四种经典线程同步问题,都是使用了Java编码。
原文网址:http://hxraid.iteye.com/blog/739265
1、生产者-消费者问题
由一个大小固定的仓库,生产者将生产的产品放入仓库,如果仓库满,则生成者被阻塞。消费者将仓库中的产品拿出来消耗,如果仓库空,则消费者被阻塞。
2、哲学家进餐问题
在1965年,Dijkstra提出并解决了一个他称之为哲学家进餐的同步问题。从那时起,每个发明新的同步原语的人都希望通过解决哲学家进餐间题来展示其同步原语的精妙之处。这个问题可以简单地描述:五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一碟通心面,由于面条很滑,所以要两把叉子才能夹住。相邻两个碟子之间有一把叉子。
哲学家的生活包括两种活动:即吃饭和思考。当一个哲学家觉得饿时,他就试图去取他左边和右边的叉子。如果成功地获得两把叉子,他就开始吃饭,吃完以后放下叉子继续思考。
3、读者-写者问题
读者一写者问题(Courtois et al., 1971)为数据库访问建立了一个模型。例如,设想一个飞机定票系统,其中有许多竞争的进程试图读写其中的数据。多个进程同时读是可以接受的,但如果一个进程正在写数据库、则所有的其他进程都不能访问数据库,即使读操作也不行。
4、理发师问题
另一个经典的问题发生在理发店里。理发店里有一位理发师,一把理发椅和n把供等候理发的顾客坐的椅子。如果没有顾客,理发师便在理发椅上睡觉,当一个顾客到来时,他必须先叫醒理发师,如果理发师正在理发时又有顾客来到,则如果有空椅子可坐,他们就坐下来等;如果没有空椅子,他就离开。