信号量的基本原理
简单点理解,信号量可看成一个整形量,表示可用资源的个数,使用资源时信号量-1,释放资源时信号量+1,以生产消费为例:
1. 假设此时缓冲区已满,sem=0,表示当前空的缓冲区个数为0
2. 生产者P1生产,sem=-1,陷入阻塞
3. 生产者P2生产,sem=-2,陷入阻塞
4. 消费者C1消费,sem=-1,唤醒P1
5. 生产者C1消费,sem=0,唤醒P2
归纳
- 信号量的值可以反映有多少线程正在等待资源, 信号量<0表示有线程因无资源而陷入等待,如sem=-2,就表示有2个生产者陷入阻塞
- 消费时sem++,执行后若sem<=0,表示之前有线程阻塞,需要唤醒,如上例中唤醒P1,P2
信号量个数与初值的选择
- 生产消费问题中需要判断空和满2个状态,首先需要2个信号量,empty表示空缓冲区个数,full表示满缓冲区个数
- 初值empty=缓冲区大小,full=0,表示初始状态缓冲区全空,满的缓冲区个数为0
- 还需要信号量mutex=1保证互斥,初值为1为什么可保持互斥请看P,V操作
引入P,V操作
P:表示申请资源
V:表示释放资源
P,V操作都是原子性的
P(semaphore s)
{
s.value--;
if(s.value<0)
当前线程阻塞
}
V(semaphore s)
{
s.value++;
if(s.value<=0)
唤醒等待在该资源上的线程
}
P,V解决生产消费
semaphore full=0;
semaphore empty=BUFF_SIZE;
semaphore mutex=1;
Producer()
{
P(empty);//要生产,则耗费空资源,empty--
P(mutex);//保证互斥
生产
.....
V(mutex);
V(full);//生产完成则产生1个满资源,full++
}
Consumer()
{
P(full);
P(mutex);
消费
...
V(mutex);
V(empty);
}
完整的java代码
java.util.concurrent.Semaphore,java中已实现好了Semaphore这个类,可在构造方法中设定初值,require和release方法对应P,V操作
import java.util.concurrent.Semaphore;
public class SynStack {
private char[] data = new char[6];
int cnt=0;
Semaphore full = new Semaphore(0);//满信号量
Semaphore empty = new Semaphore(data.length);//空信号量
Semaphore mutex = new Semaphore(1);//互斥信号量
//生产
public void push(char ch){
try {
empty.acquire();
mutex.acquire();
data[cnt]=ch;
cnt++;
System.out.println("生产线程,生产第"+cnt+"个产品,此产品是"+ch);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
mutex.release();
full.release();
}
}
//消费
public char pop(){
char ch=' ';
try {
full.acquire();
mutex.acquire();
ch=data[cnt-1];
System.out.println("消费线程,消费第"+cnt+"个产品,此产品是"+ch);
--cnt;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
mutex.release();
empty.release();
}
return ch;
}
}
public class Producer implements Runnable{
private SynStack ss =null;
public Producer(SynStack ss){
this.ss = ss;
}
@Override
public void run() {
// TODO Auto-generated method stub
char ch;
for(int i=0;i<15;++i){
ch =(char)('a'+i);
ss.push(ch);
/*try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}*/
}
}
}
public class Consumer implements Runnable{
private SynStack ss = null;
public Consumer(SynStack ss){
this.ss = ss;
}
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<15;++i){
ss.pop();
}
}
}
public class Test {
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
SynStack ss = new SynStack();
Producer producer= new Producer(ss);
Consumer consumer =new Consumer(ss);
//创建线程
Thread threadP = new Thread(producer);
Thread threadC = new Thread(consumer);
//Thread threadC2= new Thread(consumer);
//启动
threadP.start();
threadC.start();
//threadC2.start();
}
}