操做系統中的三大經典同步問題,你如何復現?

前言

做爲操做系統中的最基本模型,在面試中被要求書寫的可能性仍是很大的,若是隻是僞碼,這仍是一個簡單的問題,可是要你具體實現呢?你會使用什麼樣的方式來實現這件事情呢?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

相關文章
相關標籤/搜索