苹果-桔子线程
题目
- 桌上有一空盘,允许存放一个水果。
- 爸爸可向盘中存放苹果,也可向盘中存放桔子,
- 儿子专等吃盘中的桔子,
- 女儿专等吃盘中的苹果。
- 规定当盘空时一次只能放一只水果供吃者取用,
- 请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。
- 桌上有一空盘,最多允许存放两个水果。
- 爸爸可向盘中存放苹果,
- 妈妈可向盘中存放桔子,
- 儿子专等吃盘中的桔子,
- 女儿专等吃盘中的苹果。
- 规定当盘空时一次只能放一只水果供吃者取用,
- 请用P、V原语实现爸爸、儿子、 女儿三个并发进程的同步。
- 桌上有一空盘,只允许存放一个水果。
- 爸爸可向盘中存放苹果,
- 妈妈可向盘中存放桔子,
- 儿子专等吃盘中的桔子,
- 女儿专等吃盘中的苹果。
- 规定当盘空时一次只能放一只水果供吃者取用,
- 请用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("儿子获取资源失败!");
}
}
}
});
father.setName("父亲");
mother.setName("母亲");
daughter.setName("女儿");
son.setName("儿子");
father.start();
mother.start();
daughter.start();
son.start();
}
}
运行结果
父亲放入了一个苹果
母亲放入了一个桔子
女儿取走了一个苹果!
儿子取走了一个桔子!
父亲放入了一个苹果
母亲放入了一个桔子
儿子取走了一个桔子!
女儿取走了一个苹果!
母亲放入了一个桔子
父亲放入了一个苹果
儿子取走了一个桔子!
女儿取走了一个苹果!
母亲放入了一个桔子
父亲放入了一个苹果
女儿取走了一个苹果!
儿子取走了一个桔子!
- 和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();
}
}
运行结果
父亲放入了一个苹果
女儿取走了一个苹果!
母亲放入了一个桔子
儿子取走了一个桔子!
父亲放入了一个苹果
女儿取走了一个苹果!
母亲放入了一个桔子
儿子取走了一个桔子!
资料
Java Semaphore详解
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不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁。