在线客服
扫描二维码
下载博学谷APP扫描二维码
关注博学谷微信公众号
作为技术岗,进入一家企业不但要经历面试,还会有一些笔试题目。通过整理发现“如何保障同一资源被多个线程并发方位时的完整性?”是被提问次数比较多的问题。今天就和大家一起来学习一下这个面试题的解决方案。
常用的同步方法是采用信号或加锁机制,确保资源在任意时刻至多被一个线程访问。Java语言在多线程编程上实现了完全对象化,提供了对同步机制的良好支持。
在Java中一共有四种方法支持同步,其中前三个是同步方法,一个是管道方法。管道方法不建议使用,阻塞队列方法在问题4已有描述,现只提供前两种实现方法。
- wait()/notify()方法
- await()/signal()方法
- BlockingQueue阻塞队列方法
- PipedInputStream/PipedOutputStream
一、生产者类:
```
public class Producer extends Thread { // 每次生产的产品数量
private int num;
// 所在放置的仓库
private Storage storage;
// 构造函数,设置仓库
public Producer(Storage storage) {
this.storage = storage;
}
// 线程run函数
public void run() {
produce(num);
}
// 调用仓库Storage的生产函数
public void produce(int num) {
storage.produce(num);
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Storage getStorage() {
return storage;
}
public void setStorage(Storage storage) {
this.storage = storage;
}
}
```
二、消费者类:
```
public class Consumer extends Thread { // 每次消费的产品数量
private int num;
// 所在放置的仓库
private Storage storage;
// 构造函数,设置仓库
public Consumer(Storage storage) {
this.storage = storage;
}
// 线程run函数
public void run() {
consume(num);
}
// 调用仓库Storage的生产函数
public void consume(int num) {
storage.consume(num);
}
// get/set方法
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
public Storage getStorage() {
return storage;
}
public void setStorage(Storage storage) {
this.storage = storage;
}
}
```
仓库类:(wait()/notify()方法)
```
public class Storage { // 仓库最大存储量
private final int MAX_SIZE = 100;
// 仓库存储的载体
private LinkedList list = new LinkedList();
// 生产num个产品
public void produce(int num) {
// 同步代码段
synchronized (list) {
// 如果仓库剩余容量不足
while (list.size() + num > MAX_SIZE) {
System.out.print("【要生产的产品数量】:" + num);
System.out.println(" 【库存量】:" + list.size() + " 暂时不能执行生产任务!");
try {
list.wait();// 由于条件不满足,生产阻塞
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产条件满足情况下,生产num个产品
for (int i = 1; i <= num; ++i) {
list.add(new Object());
}
System.out.print("【已经生产产品数】:" + num);
System.out.println(" 【现仓储量为】:" + list.size());
list.notifyAll();
}
}
// 消费num个产品
public void consume(int num) {
// 同步代码段
synchronized (list) {
// 如果仓库存储量不足
while (list.size() < num) {
System.out.print("【要消费的产品数量】:" + num);
System.out.println(" 【库存量】:" + list.size() + " 暂时不能执行生产任务!");
try {
// 由于条件不满足,消费阻塞
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费条件满足情况下,消费num个产品
for (int i = 1; i <= num; ++i) {
list.remove();
}
System.out.print("【已经消费产品数】:" + num);
System.out.println(" 【现仓储)量为】:" + list.size());
list.notifyAll();
}
}
// get/set方法
public LinkedList getList() {
return list;
}
public void setList(LinkedList list) {
this.list = list;
}
public int getMAX_SIZE() {
return MAX_SIZE;
}
}
```
仓库类:(await()/signal()方法)
```
public class Storage { // 仓库最大存储量
// 仓库最大存储量
private final int MAX_SIZE = 100;
// 仓库存储的载体
private LinkedList list = new LinkedList();
// 锁
private final Lock lock = new ReentrantLock();
// 仓库满的条件变量
private final Condition full = lock.newCondition();
// 仓库空的条件变量
private final Condition empty = lock.newCondition();
// 生产num个产品
public void produce(int num) {
// 获得锁
lock.lock();
// 如果仓库剩余容量不足
while (list.size() + num > MAX_SIZE) {
System.out.print("【要生产的产品数量】:" + num);
System.out.println(" 【库存量】:" + list.size() + " 暂时不能执行生产任务!");
try {
// 由于条件不满足,生产阻塞
full.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 生产条件满足情况下,生产num个产品
for (int i = 1; i <= num; ++i) {
list.add(new Object());
}
System.out.print("【已经生产产品数】:" + num);
System.out.println(" 【现仓储量为】:" + list.size());
// 唤醒其他所有线程
full.signalAll();
empty.signalAll();
// 释放锁
lock.unlock();
}
// 消费num个产品
public void consume(int num) {
// 获得锁
lock.lock();
// 如果仓库存储量不足
while (list.size() < num) {
System.out.print("【要消费的产品数量】:" + num);
System.out.println(" 【库存量】:" + list.size() + " 暂时不能执行生产任务!");
try {
// 由于条件不满足,消费阻塞
empty.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费条件满足情况下,消费num个产品
for (int i = 1; i <= num; ++i) {
list.remove();
}
System.out.print("【已经消费产品数】:" + num);
System.out.println(" 【现仓储)量为】:" + list.size());
// 唤醒其他所有线程
full.signalAll();
empty.signalAll();
// 释放锁
lock.unlock();
}
// set/get方法
public int getMAX_SIZE() {
return MAX_SIZE;
}
public LinkedList getList() {
return list;
}
public void setList(LinkedList list) {
this.list = list;
}
以上就是“如何保障同一资源被多个线程并发方位时的完整性?”的解决方案。关注博学谷资讯页面,后期会为大家提供更多的关于java面试的题目以及解析。
— 申请免费试学名额 —
在职想转行提升,担心学不会?根据个人情况规划学习路线,闯关式自适应学习模式保证学习效果
讲师一对一辅导,在线答疑解惑,指导就业!
相关推荐 更多
Java基础 "=="和 equals 方法究竟有什么区别?
Java基础 "=="和 equals 方法究竟有什么区别?==操作符专门用来比较两个变量的值是否相等,也就是用于比较变量所对应的内存中所存储的数值是否相同,要比较两个基本类型的数据或两个引用变量是否相等,只能用==操作符。
7604
2019-05-23 11:31:26
String s = "Hello";s = s + " world!"码执行变化
String s = "Hello";s = s + " world!";这两行代码执行后原始的 String 对象中的内容变了吗? 没有。因为 String 被设计成不可变(immutable)类,所以它所有对象都是不可变对象。在这段代码中,s 原先指向一个 String 对象,内容是 "Hello",然后我们对 s 进行了+操作。
8202
2019-05-24 16:23:20
MySQL和Oracle的区别?
当你面试的时候突然被问到:MySQL和Oracle的区别?你会怎么回答?其实面试官提问的时候主要是想考察你对数据库的理解以及熟练程度,什么情况下使用 mysql,什么情况下使用 Oracle,各有什么优缺点!下面小编带大家一起分析这个问题的解答方式!
12298
2019-06-28 18:29:20
Java疑难知识分析:==与equals()的区别详解
Java编程语言是目前世界范围内使用率最高的编程语言,它是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,同时也摒弃了C++里面难易理解的多继承和指针等概念,但是相对其他部分编程语言来看,Java的学习有不少疑难知识点。这里就为大家分析一下“==”与“equals()”的区别。
5490
2019-07-05 17:44:27
线程池经典面试题整理附答案
线程池是一种多线程处理形式,它是Java开发面试中的必考知识点,尤其是在一些大厂的求职面试中,线程池是对求职者考核的重点。为了帮助大家可以更好地通过面试,本文特地为大家整理了线程池经典面试题并附上了答案,下面一起来尝试着做一做吧!
7553
2020-06-17 14:44:17