多线程信号量 Semaphore使用
对信号量只能实施三种操作:
1. 初始化(initialize),也叫做建立(create)
2. 等信号(wait),也可叫做挂起(pend)
3. 给信号(signal)或发信号(post)
分类:
整型信号量(integer semaphore):信号量是整数
记录型信号量(record semaphore):每个信号量s除一个整数值s.value(计数)外,还有一个进程等待队列s.L,其中是阻塞在该信号量的各个进程的标识
二进制信号量(binary semaphore):只允许信号量取0或1值
每个信号量至少须记录两个信息:信号量的值和等待该信号量的进程队列。它的类型定义如下:(用类PASCAL语言表述)
semaphore = record
value: integer;
queue: ^PCB;
end;
其中PCB是 进程控制块,是 操作系统为每个进程建立的 数据结构。
s.value>=0时,s.queue为空;
s.value<0时,s.value的 绝对值为s.queue中等待进程的个数;
JDK的并发包就给我提供了一个类似的信号量类——Semaphore 。
生产者-消费者问题
import java.util.concurrent.Semaphore;
class Signs{
static Semaphore empty=new Semaphore(); //信号量:记录仓库空的位置
static Semaphore full=new Semaphore(); //信号量:记录仓库满的位置
static Semaphore mutex=new Semaphore(); //临界区互斥访问信号量(二进制信号量),相当于互斥锁。
}
class Producer implements Runnable{
public void run(){
try {
while(true){
Signs.empty.acquire(); //递减仓库空信号量
Signs.mutex.acquire(); //进入临界区
System.out.println("生成一个产品放入仓库");
Signs.mutex.release(); //离开临界区
Signs.full.release(); //递增仓库满信号量
Thread.currentThread().sleep();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Consumer implements Runnable{
public void run(){
try {
while(true){
Signs.full.acquire(); //递减仓库满信号量
Signs.mutex.acquire();
System.out.println("从仓库拿出一个产品消费");
Signs.mutex.release();
Signs.empty.release(); //递增仓库空信号量
Thread.currentThread().sleep();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test{
public static void main(String[] args) {
new Thread(new Producer()).start();
new Thread(new Consumer()).start();
}
}
进一步可参考:http://blog.csdn.net/ljsspace/article/details/6702093
发现上面的程序有些问题,原因在于mutex,empty,full这几个变量虽然是static,但在多线程中任然会创建几个实例,起不到效果。后来一想,其实不会,为什么,因为
static Semaphore empty=new Semaphore(10);
是急切实例化的,jvm加载这个类的时候就创建了对象,保证了所有的线程中此对象的唯一性,参考单例的实现方式:http://www.cnblogs.com/youxin/archive/2012/11/26/2788899.html。
哲学家进餐问题
在1965年,Dijkstra提出并解决了一个他称之为哲学家进餐的同步问题。从那时起,每个发明新的同步原语的人都希望通过解决哲学家进餐间题来展示其同步原语的精妙之处。这个问题可以简单地描述:五个哲学家围坐在一张圆桌周围,每个哲学家面前都有一碟通心面,由于面条很滑,所以要两把叉子才能夹住。相邻两个碟子之间有一把叉子。
哲学家的生活包括两种活动:即吃饭和思考。当一个哲学家觉得饿时,他就试图去取他左边和右边的叉子。如果成功地获得两把叉子,他就开始吃饭,吃完以后放下叉子继续思考。
问题描述:
一个房间内有5个哲学家,他们的生活就是思考和进食。房间里有一张圆桌,中间放着一盘通心粉(假定通心粉无限多)。桌子周围放有五把椅子,分别属于五位哲学家每两位哲学家之间有一把叉子,哲学家进食时必须同时使用左右两把叉子。
解答:
进程:philosopher - 哲学家
共有的数据结构&过程:
state: array [0..4] of (think,hungry,eat);
ph: array [0..4] of semaphore;
— 每个哲学家有一个信号量,初值为0
mutex: semaphore;
— mutex保护临界区,初值=1
procedure test(i:0..4);
{
if ((state[i]=hungry) and (state[(i+1)mod 5]<>eating)
and (state[(i-1)mod 5]<>eating))
{ state[i]=eating;
V(ph[i]);
}
} philosopher(i:0..4):
{
while (true)
{
think();
p(mutex);
state[i]=hungry;
test(i);
v(mutex);
p(ph[i]);
eat();
p(mutex);
state[i]=think;
test((i-1) mod 5);
test((i+1) mod 5);
v(mutex);
}
}
更多:
http://hxraid.iteye.com/blog/739265
第一类读者写者问题:
问题描述:
一些读者和一些写者对同一个黑板进行读写。多个读者可同时读黑板,但一个时刻只能有一个写者,读者写者不能同时使用黑板。对使用黑板优先级的不同规定使读者-写者问题又可分为几类。第一类问题规定读者优先级较高,仅当无读者时允许写者使用黑板。
解答:
进程:writer - 写者进程,reader - 读者进程
共有的数据结构:
read_account:integer;
r_w,mutex: semaphore;
— r_w控制谁使用黑板,mutex保护临界区,初值都为1
reader - (读者进程):
{
while (true)
{
p(mutex);
read_account++;
if(read_account=) p(r_w);
v(mutex);
read();
p(mutex);
read_account--;
if(read_account=) v(r_w);;
v(mutex);
}
} writer - (写者进程):
{
while (true)
{
p(mutex);
write();
v(mutex);
}
}
读者-写者问题
读者一写者问题(Courtois et al., 1971)为数据库访问建立了一个模型。例如,设想一个飞机定票系统,其中有许多竞争的进程试图读写其中的数据。多个进程同时读是可以接受的,但如果一个进程正在写数据库、则所有的其他进程都不能访问数据库,即使读操作也不行。
import java.util.concurrent.Semaphore;
class Sign{
static Semaphore db=new Semaphore(1); //信号量:控制对数据库的访问
static Semaphore mutex=new Semaphore(1); //信号量:控制对临界区的访问
static int rc=0; //记录正在读或者想要读的进程数
}
class Reader implements Runnable{
public void run(){
try {
//互斥对rc的操作
Sign.mutex.acquire();
Sign.rc++; //又多了一个读线程
if(Sign.rc==1) Sign.db.acquire(); //如果是第一个读进程开始读取DB,则请求一个许可,使得写进程无法操作
DB
Sign.mutex.release();
//无临界区控制,多个读线程都可以操作DB
System.out.println("[R] "+Thread.currentThread().getName()+": read data....");
Thread.sleep(100);
//互斥对rc的操作
Sign.mutex.acquire();
Sign.rc--;
if(Sign.rc==0) Sign.db.release(); //如果最后一个读进程读完了,则释放许可,让写进程有机会操作DB
Sign.mutex.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
class Writer implements Runnable{
public void run(){
try {
//与读操作互斥访问DB
Sign.db.acquire();
System.out.println("[W] "+Thread.currentThread().getName()+": write data....");
Thread.sleep(100);
Sign.db.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Test {
public static void main(String[] args){
new Thread(new Reader()).start();
new Thread(new Reader()).start();
new Thread(new Writer()).start();
}
}
多线程信号量 Semaphore使用的更多相关文章
- java多线程--信号量Semaphore的使用
Semaphore可以控制某个共享资源可被同时访问的次数,即可以维护当前访问某一共享资源的线程个数,并提供了同步机制.例如控制某一个文件允许的并发访问的数量. 例如网吧里有100台机器,那么最多只能提 ...
- 多线程-信号量Semaphore
计数信号量用来控制同时访问某个特定资源的操作数量.Semaphore可以用于实现资源池,例如数据库连接池.我们可以构造一个固定长度的资源池,当资源池为空的时候,请求资源将会阻塞,而不是失败.当资源池非 ...
- C#多线程--信号量(Semaphore)
百度百科:Semaphore,是负责协调各个线程, 以保证它们能够正确.合理的使用公共资源.也是操作系统中用于控制进程同步互斥的量. Semaphore常用的方法有两个WaitOne()和Releas ...
- 秒杀多线程第八篇 经典线程同步 信号量Semaphore
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <且不超过最大资源数量. 第三个參数能够用来传出先前的资源计数,设为NULL表示不须要传出. 注意:当 ...
- 多线程面试题系列(8):经典线程同步 信号量Semaphore
前面介绍了关键段CS.事件Event.互斥量Mutex在经典线程同步问题中的使用.本篇介绍用信号量Semaphore来解决这个问题. 首先也来看看如何使用信号量,信号量Semaphore常用有三个函数 ...
- 转---秒杀多线程第八篇 经典线程同步 信号量Semaphore
阅读本篇之前推荐阅读以下姊妹篇: <秒杀多线程第四篇一个经典的多线程同步问题> <秒杀多线程第五篇经典线程同步关键段CS> <秒杀多线程第六篇经典线程同步事件Event& ...
- Java多线程-新特征-信号量Semaphore
简介信号量(Semaphore),有时被称为信号灯,是在多线程环境下使用的一种设施, 它负责协调各个线程, 以保证它们能够正确.合理的使用公共资源. 概念Semaphore分为单值和多值两种,前者只能 ...
- 并发编程~~~多线程~~~守护线程, 互斥锁, 死锁现象与递归锁, 信号量 (Semaphore), GIL全局解释器锁
一 守护线程 from threading import Thread import time def foo(): print(123) time.sleep(1) print('end123') ...
- Java基础教程:多线程基础(6)——信号量(Semaphore)
Java基础教程:多线程基础(6)——信号量(Semaphore) 信号量 信号量(Semaphore)由一个值和一个指针组成,指针指向等待该信号量的进程.信号量的值表示相应资源的使用情况.信号量S≥ ...
随机推荐
- XMPPFrameWork IOS 开发(二)- xcode配置
原始地址:XMPPFrameWork IOS 开发(二) 译文地址: Getting started using XMPPFramework on iOS 介绍 ios上的XMPPFramewor ...
- 【转】仿Android 联系人SideBar排序,根据拼音A-Z字母快速导航,以及输入搜索条件过滤,显示姓名的文字图片
1.首先我们把这几个工具类拷贝到自己的项目中,这些都是很常见的类: CharacterParser –这是用来把中文转成拼音的工具类 PinyinComparator –拼音首字母的比 ...
- ViewPager切换大量Fragment不刷新的问题
PagerAdapter,需要重写instantiateItem()加载视图,onDestroy()销毁视图FragmentPagerAdapter,每一个生成的Fargment都保存在内存中,也就是 ...
- slidingmenu+fragment实现经常使用的側滑效果(包含Fragment状态的保存)
一.需求 关于fragment的问题,一直想写一篇博客了.应该当初自己也是对这玩意一点都不熟悉到如今也大概知道个日常的使用的地步. 一个側滑的导航栏,内有4个条目.每个选项点击进入相应的界面,每个界面 ...
- linux 下 apt命令集详解
apt命令用法 packagename指代为软件包的名称 apt-get update 在修改/etc/apt/sources.list或/etc/apt/preferences之後运行该命令.此外您 ...
- JDK5-自动拆装箱
拆装箱:在基本类型与其对应的引用类型之间转换 装箱:Integer iObj = 5; 拆箱:int i = 5 + iObj; 装箱时,一个字节以内的数据在一个常量池中(小整数的使用频率高),即-1 ...
- GIT学习(一)-->Git产生的历史原因
首先要说:Git是目前世界上最先进的分布式版本控制系统(没有之一). git的作者:Linus,林纳斯(下图就是,先膜拜一下,因为成就可不止这一点) 他是为何要写git的呢?话说 Linus虽然创建了 ...
- JS操作DOM元素属性和方法
Dom元素基本操作方法API,先记录下,方便以后使用. W3C DOM和JavaScript很容易混淆不清.DOM是面向HTML和XML文档的API,为文档提供了结构化表示,并定义了如何通过脚本来访 ...
- Python报错:SyntaxError: Non-ASCII character '\xe5' in file
运行Python脚本总是报一下的错误: SyntaxError: Non-ASCII character '\xe5' in file 原因:Python默认是以ASCII作为编码方式的,如果在自己的 ...
- Linux sed命令在指定行前后添加内容
一.在匹配行前后加内容在包含www.baidu.com的行前面或后面添加多一行内容www.qq.com#匹配行前加sed -i '/www.baidu.com/i www.qq.com' domain ...