利用信号量解决以下3个经典多线程问题
分别为
生产者-消费者问题
写者-读者问题
哲学家就餐问题
假定在生产者和消费者之间的公用缓冲区中具有n个缓冲区,这时可利用互斥信号量mutex实现诸进程对缓冲区的互斥使用:利用信号量empty和full分别表示缓冲池中空缓冲区和满缓冲区的数量。又假定这些生产者和消费者相互等效,只要缓冲池未满,生产者便可将任务送入缓冲区,只要缓冲池未空,消费者便可从缓冲区中取走一个任务。
主体思路就是设置3个信号量,分别代表缓冲区满,缓冲区空和互斥信号量
当生产者请求生产时,先判断缓冲区是否已满,再申请互斥信号量,生产后先释放互斥信号量,再释放full信号量
同样的,消费者在消费时,也是先判断缓冲区是否为空,再申请互斥信号量,消费后先释放互斥信号量,再释放empty信号量
下面展示一些代码。
package MultiThreading;
import java.util.LinkedList;
import java.util.concurrent.Semaphore;
class Producer implements Runnable{
private ProducerAndConsumer pc;
private String name;
//-------------构造函数--------------------
public Producer(){}
public Producer(ProducerAndConsumer pc , String name){
this.pc = pc;
this.name = name;
}
//----------------------------------------
public void run(){
while(true){
try{
Thread.sleep(500);
pc.produce(name);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
class Consumer implements Runnable{
private ProducerAndConsumer pc;
private String name;
//-------------构造函数--------------------
public Consumer(){}
public Consumer(ProducerAndConsumer pc , String name){
this.pc = pc;
this.name = name;
}
//----------------------------------------
public void run(){
while(true){
try{
Thread.sleep(3000);
pc.consume(name);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
public class ProducerAndConsumer {
private LinkedList<Object> list = new LinkedList<Object>();
private Semaphore mutex = new Semaphore(1);
private Semaphore empty = new Semaphore(15);
private Semaphore full = new Semaphore(0);
public void produce(String s)
{
try {
empty.acquire();
mutex.acquire();
list.add(new Object());
System.out.println( s + "向缓冲区生产一个任务,缓冲区当前任务数" + list.size());
mutex.release();
full.release();
}
catch (Exception e) {
e.printStackTrace();
}
}
public void consume(String s)
{
try {
full.acquire();
mutex.acquire();
list.remove();
System.out.println( s + "从缓冲区消费一个任务,缓冲区当前任务数" + list.size());
mutex.release();
empty.release();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ProducerAndConsumer pc = new ProducerAndConsumer();
Thread p1 = new Thread(new Producer(pc,"生产者1号"));
Thread p2 = new Thread(new Producer(pc,"生产者2号"));
Thread c1 = new Thread(new Consumer(pc,"消费者1号"));
Thread c2 = new Thread(new Consumer(pc,"消费者2号"));
Thread c3 = new Thread(new Consumer(pc,"消费者3号"));
Thread c4 = new Thread(new Consumer(pc,"消费者4号"));
Thread c5 = new Thread(new Consumer(pc,"消费者5号"));
c1.start();
c2.start();
c3.start();
c4.start();
c5.start();
p1.start();
p2.start();
}
}
一个数据文件或记录可被多个进程共享,我们把只要求该读文件的进程称为“Reader进程”,其他进程则称为“Writer进程”。允许多个进程同时读一个共享对象,因为读操作不会使数据文件混乱。但不允许一个Writer进程和Reader进程或Writer进程同时访问共享对象。因为这种访问将会引起混乱。所谓“读者—写者”问题是指保证一个Writer进程必须与其他进程互斥地访问共享对象地同步问题。
读者-写者问题需要满足3个要求:
(1)读写互斥
(2)写写互斥
(3)允许多个读进程同时访问
这里需要用到3个信号量,wmutex,rmutex,和q 分别用来限制写者和读者的互斥,读者计数的互斥,和保证读写公平
当读者准备读时,会首先占有rmutex来计数,此时有读线程正在读,所以申请了wmutex来让写者阻塞,而后释放掉rmutex信号量,再进行操作,操作后也要登记退出,如果退出后没有读线程了,就释放掉wmutex让写线程可以进入操作。
这里加入了q信号量来保证读写公平。也就是对读者加入了另一个限制,就是q信号量,否则如果读者线程过多,那写者线程会长时间阻塞。
下面展示代码。
import java.util.concurrent.Semaphore;
//实现了3个要求
//(1)读写互斥
//(2)写写互斥
//(3)允许多个读进程同时访问
class Reader implements Runnable {
private ReaderAndWriter rw;
private String name;
public Reader() {}
public Reader(ReaderAndWriter rw,String name) {
this.rw = rw;
this.name = name;
}
public void run() {
while (true) {
try {
rw.read(name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
class Writer implements Runnable {
private ReaderAndWriter rw;
private String name;
public Writer() {}
public Writer( ReaderAndWriter rw,String name) {
this.rw = rw;
this.name = name;
}
public void run() {
while (true) {
try {
rw.write(name);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public class ReaderAndWriter {
private int readcount = 0;
private Semaphore wmutex = new Semaphore(1);
private Semaphore rmutex = new Semaphore(1);
private Semaphore q = new Semaphore(1);
public void read(String s) {
try {
q.acquire();
rmutex.acquire();
if (readcount == 0) {
wmutex.acquire();
}
readcount++;
rmutex.release();
q.release();
System.out.println(s + "----正在读");
Thread.sleep(1000*2);
System.out.println(s + "----读完了");
Thread.sleep(500);
rmutex.acquire();
readcount--;
if (readcount == 0) {
wmutex.release();
}
rmutex.release();
} catch (Exception e) {
e.printStackTrace();
}
}
public void write(String s) {
try {
q.acquire();
wmutex.acquire();
q.release();
System.out.println(s + "----正在写");
Thread.sleep(1000*2);
System.out.println(s + "----写完了");
Thread.sleep(500);
wmutex.release();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ReaderAndWriter rw = new ReaderAndWriter();
Thread r1 = new Thread(new Reader(rw,"读者1号"));
Thread r2 = new Thread(new Reader(rw,"读者2号"));
Thread r3 = new Thread(new Reader(rw,"读者3号"));
Thread w1 = new Thread(new Writer(rw,"写者1号"));
Thread w2 = new Thread(new Writer(rw,"写者2号"));
r1.start();
r2.start();
r3.start();
w1.start();
w2.start();
}
}
有五个哲学家公用一张圆桌,分别坐在周围的五张椅子上,在圆桌上有5个万和五只筷子,他们的生活方式是交替地进行思考和进餐。平时,一个哲学家进行思考,饥饿便试图取用其左右最靠近他的筷子,只有在他拿到两只筷子时才能进餐。进餐完毕时,放下筷子继续思考。
思路是让奇数号的哲学家先拿左手筷子,再拿右手筷子,让偶数号的哲学家先拿右手筷子,再拿左手筷子,这样就可以避免死锁了。
下面展示代码。
import java.util.concurrent.Semaphore;
public class PhilosopherMain {
private static Semaphore chop[] = new Semaphore[5];
private static Philosopher p[] = new Philosopher[5];
static class Philosopher implements Runnable{
private int i;
Philosopher(int i){
this.i=i;
}
@Override
public void run() {
while(true) {
try {
if(i%2!=0) {
chop[i].acquire();
System.out.println("哲学家"+i+"号拿起了了"+ i+"号筷子");
chop[(i+1)%5].acquire();
System.out.println("哲学家"+i+"号拿起了了"+ (i+1)%5+"号筷子");
}else {
chop[(i+1)%5].acquire();
System.out.println("哲学家"+i+"号拿起了了"+ (i+1)%5+"号筷子");
chop[i].acquire();
System.out.println("哲学家"+i+"号拿起了了"+ i+"号筷子");
}
System.out.println("哲学家"+i+"号正在吃饭");
Thread.sleep(1000*2);
if(i%2!=0) {
chop[i].release();
System.out.println("哲学家"+i+"号放下了"+ i+"号筷子");
chop[(i+1)%5].release();
System.out.println("哲学家"+i+"号放下了"+ (i+1)%5+"号筷子");
}else {
chop[(i+1)%5].release();
System.out.println("哲学家"+i+"号放下了"+ (i+1)%5+"号筷子");
chop[i].release();
System.out.println("哲学家"+i+"号放下了"+ i+"号筷子");
}
System.out.println("哲学家"+i+"号吃完饭了,正在思考");
Thread.sleep(1000*2);
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
public static void main(String arg[]) {
for(int i=0;i<5;i++) {
chop[i] = new Semaphore(1);
p[i] = new Philosopher(i);
}
for(int i=0;i<5;i++) {
new Thread(p[i]).start();
}
}
}