操做系统中的三大经典同步问题,你如何复现?

前言

做为操做系统中的最基本模型,在面试中被要求书写的可能性仍是很大的,若是只是伪码,这仍是一个简单的问题,可是要你具体实现呢?你会使用什么样的方式来实现这件事情呢?java

思惟导图

生产者、消费者问题

什么是生产者、消费者问题?面试

意思很简单,就是生产者给生产链生产,而消费者从生产链中拿出。那关键点已经出来了,问题就在于怎么处理这一条生产链( 正规叫法应该叫缓冲区)?

伪码实现

// 变量
list = buffer[n] // 生产链,容量为n
,mutex = 1 // 互斥使用生产链
,empty = n // 消费后剩余
,full = 0 // 生产后容量

// 两个运做对象:及对应动做
producer:product // 生产者生产
consumer:consume // 消费者消费

// 两个动做
product{
    wait(empty) // 生产链不满
    wait(mutex)
    // 生产
    signal(mutex)
    signal(full) // 给生产链加一个产品
}
consumer{
    wait(full) // 生产链不为空
    wait(mutex)
    // 消费
    signal(mutex)
    signal(empty) // 生产链中的产品又被消耗
}
复制代码

具体代码实现

class ProducerAndConsumer {
    private final int MAX_LEN = 10;
    private Queue<Integer> queue = new LinkedList<Integer>();
    Semaphore producer = new Semaphore(0);
    Semaphore consumer = new Semaphore(MAX_LEN);
    Semaphore lock = new Semaphore(1);

    // 生产者
    class Producer extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    consumer.acquire(); // 可消费的数量未满
                    lock.acquire(); // 临界区
                    queue.add(1);
                    System.out.println("consumer size:" + queue.size());
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.release();
                    producer.release();
                }
            }
        }
    }
    // 消费者
    class Consumer extends Thread {
        @Override
        public void run() {
            while (true) {
                try {
                    producer.acquire(); // 还剩余产品
                    lock.acquire(); // 临界区
                    queue.remove();
                    System.out.println("consumer size:" + queue.size());
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    lock.release();
                    consumer.release();
                }
            }
        }
    }


    public static void main(String[] args) {
        ProducerAndConsumer producerAndConsumer = new ProducerAndConsumer();
        Producer producer = producerAndConsumer.new Producer();
        Consumer consumer = producerAndConsumer.new Consumer();
        producer.start();
        consumer.start();
    }
}
复制代码

哲学家就餐问题

五把叉子,五我的,若是每一个人都拿起了叉子,那么整桌的人必然就没饭吃了,哲学家问题思考的就是这样的一个问题。bash

伪码实现

// 变量
forks = {1, 1, 1, 1, 1} // 暂定为5人

// 一个被运做对象:以及动做
fork:handle、release
Philosopher:thinking、eating

// 动做,eating和thinking是两个Thread.sleep完成
handle {
    // 左右手只要有一只被拿起,就须要等待
    // 若是不等带,就可能每一个人只拿一只
    while(forks[postion] == 0 || forks[(position + 1) % 5] == 0){
        wait() // 等待
    }
    forks[postion] == 0;
    forks[(position + 1) % 5] == 0;
}

release {
    // 吃完之后把东西放下
    forks[postion] == 1;
    forks[(position + 1) % 5] == 1;
}
复制代码

具体代码实现

public class PhilosopherEat {
    class Philosopher extends Thread {
        private String name;
        private Fork fork;

        public Philosopher(String name, Fork fork) {
            super(name);
            this.name = name;
            this.fork = fork;
        }

        @Override
        public void run() {
            // 哲学家须要完成要的一系列动做
            while (true) {
                thinking();
                fork.takeFork();
                eating();
                fork.putFork();
            }
        }

        public void eating() {
            System.out.println("I am Eating:" + name);
            try {
                //模拟吃饭
                sleep(1000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }

        public void thinking() {
            System.out.println("I am Thinking:" + name);
            try {
                //模拟思考
                sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    class Fork{
        // 5根筷子
        private boolean[] used={false,false,false,false,false,false};

        public synchronized void takeFork(){
            String name = Thread.currentThread().getName();
            int i = Integer.parseInt(name);
            // 若是左右手有一只正被使用就等待
            while(used[i]||used[(i+1)%5]){
                try {
                    wait();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            used[i ]= true;
            used[(i+1)%5]=true;
        }

        // 同时释放左右手的筷子
        public synchronized void putFork(){
            String name = Thread.currentThread().getName();
            int i = Integer.parseInt(name);
            used[i ]= false;
            used[(i+1)%5]=false;
            notifyAll();
        }
    }

    public static void main(String[] args) {
        PhilosopherEat philosopher = new PhilosopherEat();
        Fork fork = philosopher.new Fork();
        philosopher.new Philosopher("0",fork).start();
        philosopher.new Philosopher("1",fork).start();
        philosopher.new Philosopher("2",fork).start();
        philosopher.new Philosopher("3",fork).start();
        philosopher.new Philosopher("4",fork).start();
    }
}
复制代码

读者写者问题

读者写者问题针对的就是咱们的数据问题,你在wps打开一个文件,又在word打开一个文件势必会看到一个只读的模式会弹出,这就是读者写着问题的具体表现了。ide

伪码实现

// 变量
readCount // 当前读书的人数
,readLock // 读者锁
,writeLock // 写者锁

// 两个对象:及其动做
Reader:read
Writer:write

// 动做
read{
    p(readLock)
        if(readCount == 0) p(writeLock) // 第一个读者进入后,就不可修改
        readCount++
    v(readLock)
    
    // 。。。读书
    
    p(readLock)
        readCount--
        if(readCount == 0) v(writeLock) // 最后一个读者走后,能够开始修改
    v(readLock)
}

write{
    p(writeLock)
    // 。。。修改
    v(writeLock)
}
复制代码

具体代码实现

import java.util.concurrent.Semaphore;

public class ReaderAndWriter {
    public static void main(String[] args) {
        // 实现写者与写者间、读者与写者间互斥
        Semaphore wmutex = new Semaphore(1);
        // 用于改变 readCount 变量时实现互斥
        Semaphore rmutex = new Semaphore(1);
        for (int i = 0; i < 3; ++i) {
            new Reader(rmutex, wmutex).start();
            new Writer(wmutex).start();
        }
    }
}

class Reader extends Thread {
    private static int total = 0;
    private int id;
    private Semaphore rmutex, wmutex;
    private static int readCount = 0;

    public Reader(Semaphore rmutex, Semaphore wmutex) {
        id = ++total;
        this.rmutex = rmutex;
        this.wmutex = wmutex;
    }

    @Override
    public void run() {
        while (true) {
            try {
                rmutex.acquire();
                // 只有第一个读者进程须要执行 wmutex.p()
                if (readCount == 0) wmutex.acquire();
                readCount++;
                System.out.println(id + " 号读者在读");
            } catch (Exception e) {
            } finally {
                rmutex.release();
            }
            // 模拟读书
            try {
                Thread.sleep(30);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 读书人出去了
            try {
                rmutex.acquire();
                readCount--;
                System.out.println(id + " 号读者结束阅读:当前还剩 " + readCount + " 位读者在读");
                if (readCount == 0) wmutex.release();
            } catch (Exception e) {
            } finally {
                rmutex.release();
            }
        }
    }
}

class Writer extends Thread {
    private static int total = 0;
    private int id;
    private Semaphore wmutex;

    public Writer(Semaphore wmutex) {
        id = ++total;
        this.wmutex = wmutex;
    }

    @Override
    public void run() {
        while (true) {
            try {
                wmutex.acquire();
                // 执行写操做
                System.out.println(id + " 号写者正在写");
                wmutex.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 线程休眠一段时间,总不会一直改的
            try {
                Thread.sleep(300);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
复制代码

以上就是个人学习成果,若是有什么我没有思考到的地方或是文章内存在错误,欢迎与我分享。post


相关文章推荐:学习

Android自定义View,你摸的透透的了?ui

应用层中除了HTTP,你还知道点啥?this

TCP层的那些知识,你掌握了多少?spa

相关文章
相关标签/搜索