学习笔记5(模拟3个经典多线程问题)

多线程3个实例

利用信号量解决以下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();
		}
	}
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值