练习 苹果-桔子线程【操作系统】

简介: 练习 苹果-桔子线程【操作系统】

题目

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();
    }
}

运行结果

父亲放入了一个苹果
女儿取走了一个苹果!
母亲放入了一个桔子
儿子取走了一个桔子!
父亲放入了一个苹果
女儿取走了一个苹果!
母亲放入了一个桔子
儿子取走了一个桔子!

资料

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不必唤醒所有线程。缺点是处于等待队列中的线程可能会饿死,或者等很久才会获得锁。


相关实践学习
CentOS 7迁移Anolis OS 7
龙蜥操作系统Anolis OS的体验。Anolis OS 7生态上和依赖管理上保持跟CentOS 7.x兼容,一键式迁移脚本centos2anolis.py。本文为您介绍如何通过AOMS迁移工具实现CentOS 7.x到Anolis OS 7的迁移。
相关文章
|
24天前
|
调度 开发者 Python
深入浅出操作系统:进程与线程的奥秘
在数字世界的底层,操作系统扮演着不可或缺的角色。它如同一位高效的管家,协调和控制着计算机硬件与软件资源。本文将拨开迷雾,深入探索操作系统中两个核心概念——进程与线程。我们将从它们的诞生谈起,逐步剖析它们的本质、区别以及如何影响我们日常使用的应用程序性能。通过简单的比喻,我们将理解这些看似抽象的概念,并学会如何在编程实践中高效利用进程与线程。准备好跟随我一起,揭开操作系统的神秘面纱,让我们的代码运行得更加流畅吧!
|
22天前
|
算法 调度 开发者
深入理解操作系统:进程与线程的管理
在数字世界的复杂编织中,操作系统如同一位精明的指挥家,协调着每一个音符的奏响。本篇文章将带领读者穿越操作系统的幕后,探索进程与线程管理的奥秘。从进程的诞生到线程的舞蹈,我们将一起见证这场微观世界的华丽变奏。通过深入浅出的解释和生动的比喻,本文旨在揭示操作系统如何高效地处理多任务,确保系统的稳定性和效率。让我们一起跟随代码的步伐,走进操作系统的内心世界。
|
1月前
|
Linux 调度 C语言
深入理解操作系统:进程和线程的管理
【10月更文挑战第32天】本文旨在通过浅显易懂的语言和实际代码示例,带领读者探索操作系统中进程与线程的奥秘。我们将从基础知识出发,逐步深入到它们在操作系统中的实现和管理机制,最终通过实践加深对这一核心概念的理解。无论你是编程新手还是希望复习相关知识的资深开发者,这篇文章都将为你提供有价值的见解。
|
1月前
深入理解操作系统:进程与线程的管理
【10月更文挑战第30天】操作系统是计算机系统的核心,它负责管理计算机硬件资源,为应用程序提供基础服务。本文将深入探讨操作系统中进程和线程的概念、区别以及它们在资源管理中的作用。通过本文的学习,读者将能够更好地理解操作系统的工作原理,并掌握进程和线程的管理技巧。
45 2
|
1月前
|
调度 Python
深入浅出操作系统:进程与线程的奥秘
【10月更文挑战第28天】在数字世界的幕后,操作系统悄无声息地扮演着关键角色。本文将拨开迷雾,深入探讨操作系统中的两个基本概念——进程和线程。我们将通过生动的比喻和直观的解释,揭示它们之间的差异与联系,并展示如何在实际应用中灵活运用这些知识。准备好了吗?让我们开始这段揭秘之旅!
|
3月前
|
存储 消息中间件 资源调度
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
该文章总结了操作系统基础知识中的十个关键知识点,涵盖了进程与线程的概念及区别、进程间通信方式、线程同步机制、死锁现象及其预防方法、进程状态等内容,并通过具体实例帮助理解这些概念。
「offer来了」进程线程有啥关系?10个知识点带你巩固操作系统基础知识
|
2月前
|
算法 安全 调度
深入理解操作系统:进程与线程的管理
【10月更文挑战第9天】在数字世界的心脏跳动着的,不是别的,正是操作系统。它如同一位无形的指挥家,协调着硬件与软件的和谐合作。本文将揭开操作系统中进程与线程管理的神秘面纱,通过浅显易懂的语言和生动的比喻,带你走进这一复杂而又精妙的世界。我们将从进程的诞生讲起,探索线程的微妙关系,直至深入内核,理解调度算法的智慧。让我们一起跟随代码的脚步,解锁操作系统的更多秘密。
42 1
|
1月前
|
Linux 调度
探索操作系统核心:进程与线程管理
【10月更文挑战第24天】在数字世界的心脏,操作系统扮演着至关重要的角色。它不仅是计算机硬件与软件之间的桥梁,更是管理和调度资源的大管家。本文将深入探讨操作系统的两大基石——进程与线程,揭示它们如何协同工作以确保系统运行得井井有条。通过深入浅出的解释和直观的代码示例,我们将一起解锁操作系统的管理奥秘,理解其对计算任务高效执行的影响。
|
3月前
|
资源调度 算法 调度
深入浅出操作系统之进程与线程管理
【9月更文挑战第29天】在数字世界的庞大舞台上,操作系统扮演着不可或缺的角色,它如同一位精通多门艺术的导演,精心指挥着每一个进程和线程的演出。本文将通过浅显的语言,带你走进操作系统的内心世界,探索进程和线程的管理奥秘,让你对这位幕后英雄有更深的了解。
|
2月前
|
算法 调度 UED
探索操作系统中的多线程编程
【8月更文挑战第78天】在数字世界的复杂迷宫中,操作系统扮演着至关重要的角色。本文旨在揭开操作系统中多线程编程的神秘面纱,引导读者理解其概念、实现及应用。通过深入浅出的方式,我们将探讨如何在程序设计中运用多线程,以及这一技术如何优化软件性能和提升用户体验。文章将结合具体代码示例,展示多线程在实际应用中的魔力。无论你是编程新手还是资深开发者,这篇文章都将为你提供新的视角和思考路径。

热门文章

最新文章