题目
1.桌上有一空盘,允许存放一个水果。
爸爸可向盘中存放苹果,也可向盘中存放桔子,
儿子专等吃盘中的桔子,
女儿专等吃盘中的苹果。
规定当盘空时一次只能放一只水果供吃者取用,
请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。
2.桌上有一空盘,最多允许存放两个水果。
爸爸可向盘中存放苹果,
妈妈可向盘中存放桔子,
两个儿子专等吃盘中的桔子,
两个女儿专等吃盘中的苹果。
规定当盘空时一次只能放一只水果供吃者取用,
请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。
3.桌上有一空盘,只允许存放一个水果。
爸爸可向盘中存放苹果,
妈妈可向盘中存放桔子,
儿子专等吃盘中的桔子,
女儿专等吃盘中的苹果。
规定当盘空时一次只能放一只水果供吃者取用,
请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。
实现
用随机数来实现父亲是放桔子还是苹果
package os.apple_orange; import java.util.concurrent.Semaphore; /** * Eg.1、桌上有一空盘,允许存放一个水果。 * 爸爸可向盘中存放苹果,也可向盘中存放桔子, * 儿子专等吃盘中的桔子, * 女儿专等吃盘中的苹果。 * 规定当盘空时一次只能放一只水果供吃者取用, * 请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。 * * 信号量Semaphore 通过使用计数器counter来控制对共享资源的访问。 * 如果计数器大于零,则允许访问。如果为零,则拒绝访问。 * 计数器对共享资源的访问许可进行计数。因此,要访问资源,线程必须要从信号量得到许可。 */ public class ProcessTest1 { // permits 初始许可数,也就是最大访问线程数 // fair 当设置为false时,创建的信号量为非公平锁;当设置为true时,信号量是公平锁 static Semaphore empty = new Semaphore(1,false);//资源区是否为空 static Semaphore orange = new Semaphore(0,false);//资源区桔子信号 static Semaphore apple = new Semaphore(0,false);//资源区苹果信号 public static void main(String[] args) { //父亲的进程 Thread father = new Thread(new Runnable() { public void run() { while (true) { try { empty.acquire();//申请操作权限相当于wait(S) int random = Math.random() >= 0.5 ? 1 : 0; if (random == 1) { System.out.println("父亲放入了一个桔子"); Thread.sleep(1000);//休眠表示放入的过程 orange.release();//唤醒儿子的访问,相当于signal(orange) } else { System.out.println("父亲放入了一个苹果"); Thread.sleep(1000); apple.release();//唤醒女儿的访问,相当于signal(apple) } } catch (InterruptedException e) { System.out.println("父亲获取资源失败!"); e.printStackTrace(); } } } }); //女儿的进程 Thread daughter = new Thread(new Runnable() { public void run() { while (true){ try { apple.acquire();//女儿获取apple的资源,相当于wait(apple) System.out.println("女儿取走了一个苹果!"); Thread.sleep(1000);//取走的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { System.out.println("女儿获取资源失败!"); e.printStackTrace(); } } } }); //儿子的进程 Thread son = new Thread(new Runnable() { public void run() { while (true){ try { orange.acquire();//儿子获取orange资源,相当于wait(apple) System.out.println("儿子取走了一个桔子!"); Thread.sleep(1000);//取的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { e.printStackTrace(); System.out.println("儿子获取资源失败!"); } } } }); father.setName("父亲"); daughter.setName("女儿"); son.setName("儿子"); father.start(); daughter.start(); son.start(); } }
运行结果
父亲放入了一个苹果 女儿取走了一个苹果! 父亲放入了一个桔子 儿子取走了一个桔子! 父亲放入了一个桔子 儿子取走了一个桔子! 父亲放入了一个苹果 女儿取走了一个苹果!
和1的区别在于
- 增加母亲进程
//母亲的进程 Thread mother;
- 并且修改了盘子允许放水果的数量
- 又增加一个儿子
- 又增加一个女儿
//修改了盘子允许放水果的数量 static Semaphore empty = new Semaphore(1,false);//资源区是否为空
代码如下
package os.apple_orange; import java.util.concurrent.Semaphore; /** * 桌上有一空盘,最多允许存放两个水果。 * 爸爸可向盘中存放苹果, * 妈妈可向盘中存放桔子, * 两个儿子专等吃盘中的桔子, * 两个女儿专等吃盘中的苹果。 * 规定当盘空时一次只能放一只水果供吃者取用, * 请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。 */ public class ProcessTest2 { // permits 初始许可数,也就是最大访问线程数 // fair 当设置为false时,创建的信号量为非公平锁;当设置为true时,信号量是公平锁 static Semaphore empty = new Semaphore(2,false);//资源区是否为空 static Semaphore orange = new Semaphore(0,false);//资源区桔子信号 static Semaphore apple = new Semaphore(0,false);//资源区苹果信号 public static void main(String[] args) { //父亲的进程 Thread father = new Thread(new Runnable() { public void run() { while (true) { try { empty.acquire();//申请操作权限相当于wait(S) System.out.println("父亲放入了一个苹果"); Thread.sleep(1000); apple.release();//唤醒女儿的访问,相当于signal(apple) } catch (InterruptedException e) { System.out.println("父亲获取资源失败!"); e.printStackTrace(); } } } }); //母亲的进程 Thread mother = new Thread(new Runnable() { public void run() { while (true) { try { empty.acquire();//申请操作权限相当于wait(S) System.out.println("母亲放入了一个桔子"); Thread.sleep(1000);//休眠表示放入的过程 orange.release();//唤醒儿子的访问,相当于signal(orange) } catch (InterruptedException e) { System.out.println("母亲获取资源失败!"); e.printStackTrace(); } } } }); //女儿的进程 Thread daughter = new Thread(new Runnable() { public void run() { while (true){ try { apple.acquire();//女儿获取apple的资源,相当于wait(apple) System.out.println("女儿取走了一个苹果!"); Thread.sleep(1000);//取走的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { System.out.println("女儿获取资源失败!"); e.printStackTrace(); } } } }); //儿子的进程 Thread son = new Thread(new Runnable() { public void run() { while (true){ try { orange.acquire();//儿子获取orange资源,相当于wait(apple) System.out.println("儿子取走了一个桔子!"); Thread.sleep(1000);//取的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { e.printStackTrace(); System.out.println("儿子获取资源失败!"); } } } }); //女儿的进程 Thread daughter2 = new Thread(new Runnable() { public void run() { while (true){ try { apple.acquire();//女儿获取apple的资源,相当于wait(apple) System.out.println("女儿2取走了一个苹果!"); Thread.sleep(1000);//取走的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { System.out.println("女儿2获取资源失败!"); e.printStackTrace(); } } } }); //儿子的进程 Thread son2 = new Thread(new Runnable() { public void run() { while (true){ try { orange.acquire();//儿子获取orange资源,相当于wait(apple) System.out.println("儿子2取走了一个桔子!"); Thread.sleep(1000);//取的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { e.printStackTrace(); System.out.println("儿子2获取资源失败!"); } } } }); father.setName("父亲"); mother.setName("母亲"); daughter.setName("女儿"); daughter2.setName("女儿2"); son.setName("儿子"); son2.setName("儿子2"); father.start(); mother.start(); daughter.start(); daughter2.start(); son.start(); son2.start(); } }
运行结果
父亲放入了一个苹果 母亲放入了一个桔子 女儿取走了一个苹果! 儿子取走了一个桔子! 父亲放入了一个苹果 母亲放入了一个桔子 女儿2取走了一个苹果! 儿子2取走了一个桔子! 父亲放入了一个苹果 母亲放入了一个桔子 儿子取走了一个桔子! 女儿取走了一个苹果! 母亲放入了一个桔子 父亲放入了一个苹果 儿子2取走了一个桔子! 女儿2取走了一个苹果! 母亲放入了一个桔子 父亲放入了一个苹果
测试母亲同时放两个桔子的情况
因为母亲连续抢资源有点难
注释掉father.strart之后
母亲放入了一个桔子 母亲放入了一个桔子 儿子取走了一个桔子! 儿子取走了一个桔子! 母亲放入了一个桔子 母亲放入了一个桔子 儿子2取走了一个桔子! 儿子2取走了一个桔子! 母亲放入了一个桔子
和2的区别仅仅在于
//修改了盘子允许放水果的数量 static Semaphore empty = new Semaphore(1,false);//资源区是否为空
package os.apple_orange; import java.util.concurrent.Semaphore; /** * 桌上有一空盘,只允许存放一个水果。 * 爸爸可向盘中存放苹果, * 妈妈可向盘中存放桔子, * 儿子专等吃盘中的桔子, * 女儿专等吃盘中的苹果。 * 规定当盘空时一次只能放一只水果供吃者取用, * 请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。 */ public class ProcessTest3 { // permits 初始许可数,也就是最大访问线程数 // fair 当设置为false时,创建的信号量为非公平锁;当设置为true时,信号量是公平锁 static Semaphore empty = new Semaphore(1,false);//资源区是否为空 static Semaphore orange = new Semaphore(0,false);//资源区桔子信号 static Semaphore apple = new Semaphore(0,false);//资源区苹果信号 public static void main(String[] args) { //父亲的进程 Thread father = new Thread(new Runnable() { public void run() { while (true) { try { empty.acquire();//申请操作权限相当于wait(S) System.out.println("父亲放入了一个苹果"); Thread.sleep(1000); apple.release();//唤醒女儿的访问,相当于signal(apple) } catch (InterruptedException e) { System.out.println("父亲获取资源失败!"); e.printStackTrace(); } } } }); //母亲的进程 Thread mother = new Thread(new Runnable() { public void run() { while (true) { try { empty.acquire();//申请操作权限相当于wait(S) System.out.println("母亲放入了一个桔子"); Thread.sleep(1000);//休眠表示放入的过程 orange.release();//唤醒儿子的访问,相当于signal(orange) } catch (InterruptedException e) { System.out.println("母亲获取资源失败!"); e.printStackTrace(); } } } }); //女儿的进程 Thread daughter = new Thread(new Runnable() { public void run() { while (true){ try { apple.acquire();//女儿获取apple的资源,相当于wait(apple) System.out.println("女儿取走了一个苹果!"); Thread.sleep(1000);//取走的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { System.out.println("女儿获取资源失败!"); e.printStackTrace(); } } } }); //儿子的进程 Thread son = new Thread(new Runnable() { public void run() { while (true){ try { orange.acquire();//儿子获取orange资源,相当于wait(apple) System.out.println("儿子取走了一个桔子!"); Thread.sleep(1000);//取的过程 empty.release();//释放资源,相当于signal(S) } catch (InterruptedException e) { e.printStackTrace(); System.out.println("儿子获取资源失败!"); } } } }); father.setName("父亲"); mother.setName("母亲"); daughter.setName("女儿"); son.setName("儿子"); father.start(); mother.start(); daughter.start(); son.start(); } }
运行结果
父亲放入了一个苹果 女儿取走了一个苹果! 母亲放入了一个桔子 儿子取走了一个桔子! 父亲放入了一个苹果 女儿取走了一个苹果! 母亲放入了一个桔子 儿子取走了一个桔子!
资料
1、介绍
Semaphore(信号量)是用来控制同时访问特定资源的线程数量,通过协调各个线程以保证合理地使用公共资源。
Semaphore通过使用计数器来控制对共享资源的访问。 如果计数器大于0,则允许访问。 如果为0,则拒绝访问。 计数器所计数的是允许访问共享资源的许可。 因此,要访问资源,必须从信号量中授予线程许可。
2、主要方法
void acquire() :从信号量获取一个许可,如果无可用许可前将一直阻塞等待, void release():释放一个许可。
3、Semaphore构造函数
public Semaphore(int permits) { sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
permits 初始许可数,也就是最大访问线程数
fair 当设置为false时,创建的信号量为非公平锁;当设置为true时,信号量是公平锁
关于java非公平锁和公平锁可以看这篇文章:一文搞懂java中的锁
4、公平锁 和 非公平锁
公平锁是指多个线程按照申请锁的顺序来获取锁,线程直接进入队列中排队,队列中的第一个线程才能获得锁。公平锁的优点是等待锁的线程不会饿死。缺点是整体吞吐效率相对非公平锁要低,等待队列中除第一个线程以外的所有线程都会阻塞,CPU唤醒阻塞线程的开销比非公平锁大。
非公平锁是多个线程加锁时直接尝试获取锁,获取不到才会到等待队列的队尾等待。但如果此时锁刚好可用,那么这个线程可以无需阻塞直接获取到锁,所以非公平锁有可能出现后申请锁的线程先获取锁的场景。非公平锁的优点是可以减少唤起线程的开销,整体的吞吐效率高,因为线程有几率不阻塞直接获得锁,CPU不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁。