点击蓝色小字关注!
关注一下你就不会有bug!
作为操作系统中的最基本模型,在面试中被要求书写的可能性还是很大的,如果只是伪码,这还是一个简单的问题,但是要你具体实现呢?你会使用什么样的方式来实现这件事情呢?
生产者、消费者问题
什么是生产者、消费者问题? 意思很简单,就是生产者给生产链生产,而消费者从生产链中拿出。那关键点已经出来了,问题就在于怎么处理这一条生产链(正规叫法应该叫缓冲区)?伪码实现
1// 变量
2list = buffer[n] // 生产链,容量为n
3,mutex = 1 // 互斥使用生产链
4,empty = n // 消费后剩余
5,full = 0 // 生产后容量
6
7// 两个运作对象:及对应动作
8producer:product // 生产者生产
9consumer:consume // 消费者消费
10
11// 两个动作
12product{
13 wait(empty) // 生产链不满
14 wait(mutex)
15 // 生产
16 signal(mutex)
17 signal(full) // 给生产链加一个产品
18}
19consumer{
20 wait(full) // 生产链不为空
21 wait(mutex)
22 // 消费
23 signal(mutex)
24 signal(empty) // 生产链中的产品又被消耗
25}
具体代码实现
1class ProducerAndConsumer {
2 private final int MAX_LEN = 10;
3 private Queue queue = new LinkedList(); 4 Semaphore producer = new Semaphore(0); 5 Semaphore consumer = new Semaphore(MAX_LEN); 6 Semaphore lock = new Semaphore(1); 7 8 // 生产者 9 class Producer extends Thread {10 @Override11 public void run() {12 while (true) {13 try {14 consumer.acquire(); // 可消费的数量未满15 lock.acquire(); // 临界区16 queue.add(1);17 System.out.println("consumer size:" + queue.size());18 Thread.sleep(200);19 } catch (InterruptedException e) {20 e.printStackTrace();21 } finally {22 lock.release();23 producer.release();24 }25 }26 }27 }28 // 消费者29 class Consumer extends Thread {30 @Override31 public void run() {32 while (true) {33 try {34 producer.acquire(); // 还剩余产品35 lock.acquire(); // 临界区36 queue.remove();37 System.out.println("consumer size:" + queue.size());38 Thread.sleep(200);39 } catch (InterruptedException e) {40 e.printStackTrace();41 } finally {42 lock.release();43 consumer.release();44 }45 }46 }47 }484950 public static void main(String[] args) {51 ProducerAndConsumer producerAndConsumer = new ProducerAndConsumer();52 Producer producer = producerAndConsumer.new Producer();53 Consumer consumer = producerAndConsumer.new Consumer();54 producer.start();55 consumer.start();56 }57}
哲学家就餐问题
五把叉子,五个人,如果每个人都拿起了叉子,那么整桌的人必然就没饭吃了,哲学家问题思考的就是这样的一个问题。
伪码实现
1// 变量
2forks = {1, 1, 1, 1, 1} // 暂定为5人
3
4// 一个被运作对象:以及动作
5fork:handle、release
6Philosopher:thinking、eating
7
8// 动作,eating和thinking是两个Thread.sleep完成
9handle {
10 // 左右手只要有一只被拿起,就需要等待
11 // 如果不等带,就可能每个人只拿一只
12 while(forks[postion] == 0 || forks[(position + 1) % 5] == 0){
13 wait() // 等待
14 }
15 forks[postion] == 0;
16 forks[(position + 1) % 5] == 0;
17}
18
19release {
20 // 吃完以后把东西放下
21 forks[postion] == 1;
22 forks[(position + 1) % 5] == 1;
23}
具体代码实现
1public class PhilosopherEat {
2 class Philosopher extends Thread {
3 private String name;
4 private Fork fork;
5
6 public Philosopher(String name, Fork fork) {
7 super(name);
8 this.name = name;
9 this.fork = fork;
10 }
11
12 @Override
13 public void run() {
14 // 哲学家需要完成要的一系列动作
15 while (true) {
16 thinking();
17 fork.takeFork();
18 eating();
19 fork.putFork();
20 }
21 }
22
23 public void eating() {
24 System.out.println("I am Eating:" + name);
25 try {
26 //模拟吃饭
27 sleep(1000);
28 } catch (InterruptedException e) {
29 // TODO Auto-generated catch block
30 e.printStackTrace();
31 }
32 }
33
34 public void thinking() {
35 System.out.println("I am Thinking:" + name);
36 try {
37 //模拟思考
38 sleep(1000);
39 } catch (InterruptedException e) {
40 e.printStackTrace();
41 }
42 }
43 }
44
45 class Fork{
46 // 5根筷子
47 private boolean[] used={false,false,false,false,false,false};
48
49 public synchronized void takeFork(){
50 String name = Thread.currentThread().getName();
51 int i = Integer.parseInt(name);
52 // 如果左右手有一只正被使用就等待
53 while(used[i]||used[(i+1)%5]){
54 try {
55 wait();
56 } catch (InterruptedException e) {
57 e.printStackTrace();
58 }
59 }
60 used[i ]= true;
61 used[(i+1)%5]=true;
62 }
63
64 // 同时释放左右手的筷子
65 public synchronized void putFork(){
66 String name = Thread.currentThread().getName();
67 int i = Integer.parseInt(name);
68 used[i ]= false;
69 used[(i+1)%5]=false;
70 notifyAll();
71 }
72 }
73
74 public static void main(String[] args) {
75 PhilosopherEat philosopher = new PhilosopherEat();
76 Fork fork = philosopher.new Fork();
77 philosopher.new Philosopher("0",fork).start();
78 philosopher.new Philosopher("1",fork).start();
79 philosopher.new Philosopher("2",fork).start();
80 philosopher.new Philosopher("3",fork).start();
81 philosopher.new Philosopher("4",fork).start();
82 }
83}
读者写者问题
读者写者问题针对的就是我们的数据问题,你在 wps 打开一个文件,又在 word 打开一个文件势必会看到一个只读的模式会弹出,这就是读者写着问题的具体表现了。伪码实现
1// 变量
2readCount // 当前读书的人数
3,readLock // 读者锁
4,writeLock // 写者锁
5
6// 两个对象:及其动作
7Reader:read
8Writer:write
9
10// 动作
11read{
12 p(readLock)
13 if(readCount == 0) p(writeLock) // 第一个读者进入后,就不可修改
14 readCount++
15 v(readLock)
16
17 // 。。。读书
18
19 p(readLock)
20 readCount--
21 if(readCount == 0) v(writeLock) // 最后一个读者走后,可以开始修改
22 v(readLock)
23}
24
25write{
26 p(writeLock)
27 // 。。。修改
28 v(writeLock)
29}
具体代码实现
1import java.util.concurrent.Semaphore;
2
3public class ReaderAndWriter {
4 public static void main(String[] args) {
5 // 实现写者与写者间、读者与写者间互斥
6 Semaphore wmutex = new Semaphore(1);
7 // 用于改变 readCount 变量时实现互斥
8 Semaphore rmutex = new Semaphore(1);
9 for (int i = 0; i 3; ++i) {
10 new Reader(rmutex, wmutex).start();
11 new Writer(wmutex).start();
12 }
13 }
14}
15
16class Reader extends Thread {
17 private static int total = 0;
18 private int id;
19 private Semaphore rmutex, wmutex;
20 private static int readCount = 0;
21
22 public Reader(Semaphore rmutex, Semaphore wmutex) {
23 id = ++total;
24 this.rmutex = rmutex;
25 this.wmutex = wmutex;
26 }
27
28 @Override
29 public void run() {
30 while (true) {
31 try {
32 rmutex.acquire();
33 // 只有第一个读者进程需要执行 wmutex.p()
34 if (readCount == 0) wmutex.acquire();
35 readCount++;
36 System.out.println(id + " 号读者在读");
37 } catch (Exception e) {
38 } finally {
39 rmutex.release();
40 }
41 // 模拟读书
42 try {
43 Thread.sleep(30);
44 } catch (InterruptedException e) {
45 e.printStackTrace();
46 }
47 // 读书人出去了
48 try {
49 rmutex.acquire();
50 readCount--;
51 System.out.println(id + " 号读者结束阅读:当前还剩 " + readCount + " 位读者在读");
52 if (readCount == 0) wmutex.release();
53 } catch (Exception e) {
54 } finally {
55 rmutex.release();
56 }
57 }
58 }
59}
60
61class Writer extends Thread {
62 private static int total = 0;
63 private int id;
64 private Semaphore wmutex;
65
66 public Writer(Semaphore wmutex) {
67 id = ++total;
68 this.wmutex = wmutex;
69 }
70
71 @Override
72 public void run() {
73 while (true) {
74 try {
75 wmutex.acquire();
76 // 执行写操作
77 System.out.println(id + " 号写者正在写");
78 wmutex.release();
79 } catch (InterruptedException e) {
80 e.printStackTrace();
81 }
82 // 线程休眠一段时间,总不会一直改的
83 try {
84 Thread.sleep(300);
85 } catch (InterruptedException e) {
86 e.printStackTrace();
87 }
88 }
89 }
90}
原文链接 https://juejin.im/post/5e59fe90f265da574657d9e8
▼往期精彩回顾▼问懵逼:JVM中类的主动使用与被动使用?什么样的对象才能作为GC ROOT?GC ROOTS 有哪些?
点击左下角阅读原文查看历史经典技术问题汇总,看完顺手走一波PYQ呀~