首页
壁纸
留言板
友链
更多
统计归档
Search
1
TensorBoard:训练日志及网络结构可视化工具
12,588 阅读
2
主板开机跳线接线图【F_PANEL接线图】
7,032 阅读
3
Linux使用V2Ray 原生客户端
6,147 阅读
4
移动光猫获取超级密码&开启公网ipv6
4,675 阅读
5
NVIDIA 显卡限制功率
3,130 阅读
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
登录
/
注册
Search
标签搜索
好物分享
学习笔记
linux
MySQL
nvidia
typero
内网穿透
webdav
vps
java
cudann
gcc
cuda
树莓派
CNN
图像去雾
ssh安全
nps
暗通道先验
阿里云
jupiter
累计撰写
354
篇文章
累计收到
71
条评论
首页
栏目
好物分享
实用教程
linux使用
wincmd
学习笔记
mysql
java学习
nginx
综合面试题
大数据
网络知识
linux
放码过来
python
javascript
java
opencv
蓝桥杯
leetcode
深度学习
开源模型
相关知识
数据集和工具
模型轻量化
语音识别
计算机视觉
杂七杂八
硬件科普
主机安全
嵌入式设备
其它
bug处理
页面
壁纸
留言板
友链
统计归档
搜索到
8
篇与
的结果
2023-02-18
Spring&SpringMVC高频面试题梳理
0.题目汇总Spring的IOC和AOP机制?Spring中Autowired和Resouree关键字的区别和联系?依赖注入的方式有几种,各是什么?讲一下什么是Spring?Spring框架中都用到了哪些设计模式?Spring框架中都用到了哪些设计模式?1.Spring的IOC和AOP机制?IOC(Inversion of Control):IOC是控制反转的意思,这是一种面向对象编程的设计思想,可以帮我们维护对象与对象之间的依赖关系,降低对象之间的耦合度。简单来说,就是将原本在程序中自己手动创建对象的控制权,交由 Spring 框架来管理,Spring IOC 容器就像是一个工厂一样,当我们需要创建一个对象的时候,只需要配置好配置文件/注解即可,完全不用考虑对象是如何被创建出来的。 IOC 容器负责创建对象,将对象连接在一起,配置这些对象,并从创建中处理这些对象的整个生命周期,直到它们被完全销毁。在spring中IOC是通过DI(Dependency Injection)/依赖注入实现的。AOP(Aspect Oriented Programing)是面向切面编程思想,这种思想是对OOP的补充,它可以在OOP的基础上进一步提高编程的效率。简单来说,它可以统一解决一批组件的共性需求(如权限检查、记录日志、事务管理等)。它利用一种称为"横切"的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为"Aspect",即切面。所谓"切面",简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。2.Spring中Autowired和Resouree关键字的区别和联系?联系@Autowired和@Resource注解都是作为bean对象注入的时候使用的两者都可以声明在字段和setter方法上注意:如果声明在字段上,那么就不需要再写setter方法。但是本质上,该对象还是作为set方法的实参,通过执行set方法注入,只是省略了setter方法罢了区别@Autowired注解是Spring提供的,而@Resource注解是J2EE本身提供的@Autowired注解默认通过byType方式注入,而@Resource注解默认通过byName方式注入@Autowired注解注入的对象需要在IOC容器中存在,否则需要加上属性required=false,表示忽略当前要注入的bean,如果有直接注入,没有跳过,不会报错讲一下什么是Spring?3.依赖注入的方式有几种,各是什么?构造器注入将被依赖对象通过构造函数的参数注入给依赖对象,并且在初始化对象的时候注入。<!-- 第一种根据index参数下标设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- index指构造方法 , 下标从0开始 --> <constructor-arg index="0" value="kuangshen2"/> </bean> <!-- 第二种根据参数名字设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <!-- name指参数名 --> <constructor-arg name="name" value="kuangshen2"/> </bean> <!-- 第三种根据参数类型设置 --> <bean id="userT" class="com.kuang.pojo.UserT"> <constructor-arg type="java.lang.String" value="kuangshen2"/> </bean>setter方法注入通过调用成员变量提供的setter函数将被依赖对象注入给依赖类。<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="address" class="com.hui.pojo.Address"> <property name="address" value="背景"/> </bean> <!-- 依赖注入之set注入 --> <bean id="student" class="com.hui.pojo.Student"> <!--1. 普通值注入,value--> <property name="name" value="李家辉"/> <!--2. bean注入,ref--> <property name="address" ref="address"/> <!--3. 数组注入,array-value --> <property name="books"> <array> <value>李</value> <value>家</value> <value>辉</value> </array> </property> <!--list注入,list-value --> <property name="hobbys"> <list> <value>语文</value> <value>数学</value> <value>英语</value> </list> </property> <!--Map注入,map-entry-key-value --> <property name="card"> <map> <entry key="身份证" value="123"/> <entry key="银行卡" value="456"/> </map> </property> <!--Set注入,set-value --> <property name="games"> <set> <value>IOC</value> <value>DI</value> </set> </property> <!--null注入--> <property name="wife"> <null/> </property> <!----> <property name="info"> <props> <prop key="学号">2019</prop> <prop key="username">男</prop> <prop key="password">123456</prop> </props> </property> </bean> </beans>c命名空间与p命名空间p命名空间就是对应setter注入(property);c命名空间就是对应构造方法注入(constructor-arg)。<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xmlns:c="http://www.springframework.org/schema/c" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--p命名空间注入,可以直接注入属性的值:property。p命名空间注入就是对应set注入的属性注入--> <!--需要导入约束 xmlns:p="http://www.springframework.org/schema/p" --> <bean id="user" class="com.hui.pojo.User" p:name="李家辉" p:age="22"/> <!--c命名空间注入,对应所有的构造器注入,constructor-arg --> <!--需要导入约束 xmlns:c="http://www.springframework.org/schema/c" --> <bean id="user1" class="com.hui.pojo.User" c:name="李家毅" c:age="22"/> </beans>autowire byName (按名称自动装配)<bean id="user" class="com.kuang.pojo.User" autowire="byName"> <property name="str" value="qinjiang"/> </bean>autowire byType (按类型自动装配)<bean id="dog" class="com.kuang.pojo.Dog"/> <bean id="cat" class="com.kuang.pojo.Cat"/> <bean id="cat2" class="com.kuang.pojo.Cat"/> <bean id="user" class="com.kuang.pojo.User" autowire="byType"> <property name="str" value="qinjiang"/> </bean>使用注解实现自动装配@Autowired+@Qualifier@Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配@Qualifier不能单独使用。@Resource@Resource如有指定的name属性,先按该属性进行byName方式查找装配;其次再进行默认的byName方式进行装配;如果以上都不成功,则按byType的方式自动装配。都不成功,则报异常。4.讲一下什么是Spring?Spring是一个轻量级的IoC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。简单来说,它是一个容器框架,用来装 javabean(java对象),中间层框架(万能胶)可以起一个连接作用,可以把各种技术粘合在一起运用。主要由以下几个模块组成:Spring Core:SpringCore模块是Spring的核心容器,它实现了IOC模式,提供了Spring框架的基础功能。此模块中包含的BeanFactory类是Spring的核心类,负责JavaBean的配置与管理。它采用Factory模式实现了IOC即依赖注入。Spring AOP:Spring在它的AOP模块中提供了对面向切面编程的丰富支持,Spring AOP 模块为基于 Spring 的应用程序中的对象提供了事务管理服务。通过使用 Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中,可以自定义拦截器、切点、日志等操作Spring DAO:提供了一个JDBC的抽象层和异常层次结构,消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析, 用于简化JDBCSpring ORM:对现有的ORM框架的支持;Spring Context:提供框架式的Bean访问方式,以及企业级功能(JNDI、定时任务等);Spring Web:提供了基本的面向Web的综合特性,例如多方文件上传;Spring MVC:提供面向Web应用的Model-View-Controller实现。5.解释Spring支持的几种bean的作用域。类型说明singleton在Spring容器中仅存在一个实例,即Bean以单例的形式存在。prototype每次调用getBean()时,都会执行new操作,返回一个新的实例。request每次HTTP请求都会创建一个新的Bean。session同一个HTTP Session共享一个Bean,不同的HTTP Session使用不同的Bean。globalSession同一个全局的Session共享一个Bean,一般用于Portlet环境6.Spring框架中都用到了哪些设计模式?工厂模式:ApplicationContext类使用工厂模式创建Bean对象单例模式:Spring中的Bean的作用域默认就是单例Singleton的原型模式:在 spring 中用到的原型模式有: scope="prototype" ,每次获取的是通过克隆生成的新实例,对其进行修改时对原有实例对象不造成任何影响。代理模式:Spring AOP基于动态代理实现的模板模式:Spring中以Template结尾的类,比如jdbcTemplate、SqlSessionTemplate等,都是使用了模板方法模式装饰器模式(动态地给对象添加一些额外的属性或者行为 和继承相比,装饰器模式更加灵活):Spring中配置DataSource时 ,DataSource可以是不同的数据库和数据源.为了在少修改原有类的代码下动态切换不同的数据源,这时就用到了装饰器模式责任链模式:DispatcherServlet 中的 doDispatch() 方法中获取与请求匹配的处理器HandlerExecutionChain,this.getHandler() 方法的处理使用到了责任链模式。观察者模式:Spring 中的 Event 和 Listener7.什么是MVC?MVC是一种架构模式,在这种模式下软件被分为三层,即Model(模型)、View(视图)、Controller(控制器)。Model代表的是数据,View代表的是用户界面,Controller代表的是数据的处理逻辑,它是Model和View这两层的桥梁。将软件分层的好处是,可以将对象之间的耦合度降低,便于代码的维护。8.谈谈对 Spring MVC 的理解Spring MVC 是一款很优秀的 MVC 框架。可以让我们的开发更简洁,而且它和 Spring 是无缝集成,是 Spring 的一个子模块,是我们上面提到 Spring 大家族中 Web 模块。Spring MVC 框架主要由 DispatcherServlet 、处理器映射、处理器(控制器)、视图解析器、视图组成。Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。springMVC执行流程9.SpringMVC怎么样设定重定向和转发的?Spring MVC 请求方式分为转发、重定向 2 种,分别使用 forward 和 redirect 关键字在 controller 层进行处理。重定向是将用户从当前处理请求定向到另一个视图(例如 JSP)或处理请求,以前的请求(request)中存放的信息全部失效,并进入一个新的 request 作用域;转发是将用户对当前处理的请求转发给另一个视图或处理请求,以前的 request 中存放的信息不会失效。转发是服务器行为,重定向是客户端行为。1)转发过程客户浏览器发送 http 请求,Web 服务器接受此请求,调用内部的一个方法在容器内部完成请求处理和转发动作,将目标资源发送给客户;在这里转发的路径必须是同一个 Web 容器下的 URL,其不能转向到其他的 Web 路径上,中间传递的是自己的容器内的 request。在客户浏览器的地址栏中显示的仍然是其第一次访问的路径,也就是说客户是感觉不到服务器做了转发的。转发行为是浏览器只做了一次访问请求。2)重定向过程客户浏览器发送 http 请求,Web 服务器接受后发送 302 状态码响应及对应新的 location 给客户浏览器,客户浏览器发现是 302 响应,则自动再发送一个新的 http 请求,请求 URL 是新的 location 地址,服务器根据此请求寻找资源并发送给客户。在这里 location 可以重定向到任意 URL,既然是浏览器重新发出了请求,那么就没有什么 request 传递的概念了。在客户浏览器的地址栏中显示的是其重定向的路径,客户可以观察到地址的变化。重定向行为是浏览器做了至少两次的访问请求。在 Spring MVC 框架中,重定向与转发的示例代码如下:@RequestMapping(“/login”) public String login() { //转发到一个请求方法(同一个控制器类可以省略/index/) return “forward:/index/isLogin”; } @RequestMapping(“/isLogin”) public String isLogin() { //重定向到一个请求方法 return “redirect:/index/isRegister”; }在 Spring MVC 框架中,控制器类中处理方法的 return 语句默认就是转发实现,只不过实现的是转发到视图。@RequestMapping(“/register”) public String register() { return “register”; //转发到register.jsp }10.SpringMVC常用的注解有哪些?组件型注解:作用:被注解的类将被spring初始话为一个bean,然后统一管理。@Component 在类定义之前添加@Component注解,他会被spring容器识别,并转为bean。@Repository 对Dao实现类进行注解 (特殊的@Component)@Service 用于对业务逻辑层进行注解, (特殊的@Component)@Controller 用于控制层注解 , (特殊的@Component)==待完成==参考资料@Autowired和@Resource注解的区别和联系(十分详细,不看后悔)依赖注入三种方式设计模式_spring框架中常用的8种设计模式
2023年02月18日
606 阅读
0 评论
0 点赞
2022-09-14
操作系统高频面试题梳理
0.题目汇总线程和进程的区别?进程之间的通信方式说一说进程的状态说说僵尸进程和孤儿进程。死锁的必要条件周转时间和带权周转时间的计算页面置换算法1.线程和进程的区别?1、根本区别: 进程是操作系统资源分配和独立运行的最小单位;线程是任务调度和系统执行的最小单位。2、地址空间区别: 每个进程都有独立的地址空间,一个进程崩溃不影响其它进程;一个进程中的多个线程共享该 进程的地址空间,一个线程的非法操作会使整个进程崩溃。3、上下文切换开销区别: 每个进程有独立的代码和数据空间,进程之间上下文切换开销较大;线程组共享代码和数据空间,线程之间切换的开销较小。2.进程之间的通信方式每个进程的用户地址空间都是独立的,一般而言是不能互相访问的,但内核空间是每个进程都共享的,所以进程之间要通信必须通过内核。管道(内核中的缓冲区)管道本质上就是内核中的一个缓冲区,程序通过对缓存进行读写完成通信匿名管道:在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利。// 创建匿名管道,并返回了两个描述符,一个是管道的读取端描述符 fd[0],另一个是管道的写入端描述符 fd[1] int fd[2]; if(pipe(fd)==-1) ERR_EXIT("pipe error");看到这,你可能会有疑问了,这两个描述符都是在一个进程里面,并没有起到进程间通信的作用,怎么样才能使得管道是跨过两个进程的呢?可以使用 fork 创建子进程,创建的子进程会复制父进程的文件描述符,这样就做到了两个进程各有两个「 fd[0] 与 fd[1]」,两个进程就可以通过各自的 fd 写入和读取同一个管道文件实现跨进程通信了。可以使用 fork 创建的两个子进程之间也可以使用匿名管道实现通信。命名管道: 在内核中申请一块固定大小的缓冲区,程序拥有写入和读取的权利,没有血缘关系的进程也可以进程间通信。mkfifo("tp",0644); int infd; infd = open("abc",o_RDONLY); if (infd ==-1)ERR_EXIT("open"); int outfd; outfd = open ("tp",O_WRONLY);优点实现简单,自带同步互斥机制缺点每个消息的最大长度是有上限的(MSGMAX)效率低下,不适合进程间频繁的交换数据半双工通信,同一时刻只有一个进程可以进行读写。(可以用两个管道实现双向通信)消息队列(内核中的队列)在内核中创建一队列,不同的进程可以通过句柄去访问这个队列,队列中每个元素是一个数据报。优点消息队列可实现双向通信。消息队列允许一个或多个进程写入或者读取消息。缺点每个消息的最大长度是有上限的(MSGMAX)消息队列的总数也有⼀个上限(MSGMNI)存在用户态和内核态之间的数据拷贝问题。进程往消息队列写入数据时,会发送用户态拷贝数据到内核态的过程,同理读取数据时会发生从内核态到用户态拷贝数据的过程。共享内存将同一块物理内存一块映射到不同的进程的虚拟地址空间中,实现不同进程间对同一资源的共享。这样一个进程写入的东西,另一个进程马上就能够看到,不需要进行拷贝。优点速度快:不需要陷入内核态或者系统调用,大大提高了通信的速度,享有最快的进程间通信方式之名缺点多进程竞争同个共享资源会造成数据的错乱:如果有多个进程同时往共享内存写入数据,有可能先写的进程的内容会被其他进程覆盖。信号量(内核中的信号量集合)在内核中创建一个信号量集合(本质是个数组),数组的元素(信号量)都是1,使用P操作进行-1,使用V操作+1,主要用于实现进程间的互斥与同步,而不是用于缓存进程间通信的数据。P(sv):如果sv的值⼤大于零,就给它减1;如果它的值为零,就挂起该进程的执⾏ 。V(sv):如果有其他进程因等待sv而被挂起,就让它恢复运⾏,如果没有进程因等待sv⽽挂起,就给它加1。信号上面说的进程间通信,都是常规状态下的工作模式。对于异常情况下的工作模式,就需要用「信号」的方式来通知进程。信号是进程间通信机制中唯一的异步通信机制。在Linux中,为了响应各种事件,提供了几十种信号,可以通过kill -l命令查看。$ kill -l 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX运行在 shell 终端的进程,我们可以通过键盘输入某些组合键的时候,给进程发送信号。例如Ctrl+C 产生 SIGINT 信号,表示终止该进程;Ctrl+Z 产生 SIGTSTP 信号,表示停止该进程,但还未结束;如果进程在后台运行,可以通过 kill 命令的方式给进程发送信号,但前提需要知道运行中的进程 PID 号,例如:kill -9 1050 ,表示给 PID 为 1050 的进程发送 SIGKILL 信号,用来立即结束该进程;Socket前面提到的管道、消息队列、共享内存、信号量和信号都是在同一台主机上进行进程间通信,那要想跨网络与不同主机上的进程之间通信,就需要 Socket 通信了。实际上,Socket 通信不仅可以跨网络与不同主机的进程间通信,还可以在同主机上进程间通信。针对 TCP 协议通信的 socket 编程模型针对 UDP 协议通信的 socket 编程模型3.说一说进程的状态创建状态:进程在创建时需要申请一个空白PCB,向其中填写控制和管理进程的信息,完成资源分配。如果创建工作无法完成,比如资源无法满足,就无法被调度运行,把此时进程所处状态称为创建状态就绪状态:进程已经准备好,已分配到所需资源,只要分配到CPU就能够立即运行执行状态:进程处于就绪状态被调度后,进程进入执行状态阻塞状态:正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用终止状态:进程结束,或出现错误,或被系统终止,进入终止状态。无法再执行4.说说僵尸进程和孤儿进程。我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程。子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程 到底什么时候结束。 当一个 进程完成它的工作终止之后,它的父进程需要调用wait()或者waitpid()系统调用取得子进程的终止状态。孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。僵尸进程:一个进程使用fork创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。5.死锁的必要条件互斥条件:一个资源每次只能被一个进程使用; 请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放;不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺;循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系;6.周转时间和带权周转时间的计算$$ 周转时间=作业完成时刻-作业到达时刻\\ 带权周转时间=\frac{周转时间}{服务时间}\\ 平均周转时间=\frac{作业周转总时间}{作业个数}\\ 平均带权周转时间=\frac{带权周转总时间}{作业个数}\\ $$7.页面置换算法在进程运行的过程当中,进程所要访问的页面不在内存中,我们就需要把这个不存在的页面调入内存,但内存已经没有空闲空间了,这时候就要求系统从内存中调出一个页面,将其移入磁盘的对换区中。将哪个页面调出来,就要通过算法来确定。我们把选择换出页面的算法就叫做页面置换算法。先进先出置换算法FIFO思想:先进先出算法总是淘汰最先进入内存的页面。出发点是近期调入的页面被再次访问的概率要大于早期调入页面的概率。例题:考虑下述页面走向:1,2,3,4,2,1,5,6,2,1,2,3,7,6,3,2,1,2,3,6。当内存块数量分别为3时,试问先进先出置换算法(FIFO)的缺页次数是多少?最近最少使用页面置换算法LRU思想:每次选择内存中离当前时刻最久未使用过的页面淘汰,依据的原理是局部性原理例题:考虑下述页面走向:1,2,3,4,2,1,5,6,2,1,2,3,7,6,3,2,1,2,3,6。当内存块数量分别为3时,试问最近最少使用页面置换算法(LRU)的缺页次数是多少?最佳置换算法OPT思想:选择的被淘汰页面是以后用不使用的,或者是在未来最长一段时间内不再被访问的页面。例题:考虑下述页面走向:1,2,3,4,2,1,5,6,2,1,2,3,7,6,3,2,1,2,3,6。当内存块数量分别为3时,试问最佳置换法(OPT)的缺页次数是多少?Clock置换算法略参考资料面试题:进程、线程及协程的区别六种进程间通信方式进程间的通信方式(六种)
2022年09月14日
335 阅读
0 评论
0 点赞
2022-09-07
JavaSE高频面试题梳理-2.多线程与并发
0.题目汇总Java中实现多线程有几种方法如何停止一个正在运行的线程notify()和notifyAll()有什么区别?sleep()和wait()有什么区别volatile是什么?可以保证有序性吗?Thread 类中的start()和run()方法有什么区别?为什么wait, notify 和notifyAll这些方法不在thread类里面?为什么wait, notify方法要在同步块中调用?Java中interrupted和isInterruptedd方法的区别Java中synchronized和ReentrantLock有什么不同?有三个线程T1, T2, T3,如何保证顺序执行?SynchronizedMap 和 ConcurrentHashMap 有什么区别?Thread类中的yield方法有什么作用?Java线程池中submit()和execute()方法有什么区别?volatile关键字的作用?常用的线程池有哪些?简述一下你对线程池的理解Runnable接口和Callable接口的区别1.Java中实现多线程有几种方法继承 Thread 类实现 Runnable 接口实现 Callable 接口:Runnable接口是没有返回值的,Callable有返回值,可以抛出异常;Thread类并不接受Callable对象。可以使用FutureTask类实现Runnable接口和Future接口;线程池线程池的概念:线程池就是首先创建一些线程,它们的集合称为线程池。使用线程池可以很好地提高性能,线程池在系统启动时即创建大量空闲的线程,程序将一个任务传给线程池,线程池就会启动一条线程来执行这个任务,执行结束以后,该线程并不会死亡,而是再次返回线程池中成为空闲状态,等待执行下一个任务。使用线程池的原因:多线程运行时间,系统不断的启动和关闭新线程,成本非常高,会过渡消耗系统资源,以及过渡切换线程的危险,从而可能导致系统资源的崩溃。这时,线程池就是最好的选择了。2.如何停止一个正在运行的线程使用退出标志,使线程正常退出,也就是当run方法完成后线程终止。使用stop方法强行终止,但是不推荐这个方法,因为这个方法是不安全的,而且是已被废弃的方法。使用interrupt方法中断线程3.notify()和notifyAll()有什么区别?二者都是用来用来唤醒调用wait()方法进入等待锁资源队列的线程,区别在于:notify():唤醒正在等待此对象监视器的单个线程。 如果有多个线程在等待,则选择其中一个随机唤醒(由调度器决定),唤醒的线程享有公平竞争资源的权利notifyAll():唤醒正在等待此对象监视器的所有线程,唤醒的所有线程公平竞争资源4.sleep()和wait()有什么区别sleep()和wait()都是线程暂停执行的方法。1、这两个方法来自不同的类分别是Thread和Object,sleep方法属于Thread类中的静态方法,wait属于Object的成员方法。2、sleep()是线程类(Thread)的方法,不涉及线程通信,调用时会暂停此线程指定的时间,但监控依然保持,不会释放对象锁,到时间自动恢复;wait()是Object的方法,用于线程间的通信,调用时会放弃对象锁,进入等待队列,待调用notify()/notifyAll()唤醒指定的线程或者所有线程,才进入对象锁定池准备获得对象锁进入运行状态。3、wait,notify和notifyAll只能在同步控制方法或者同步控制块里面使用,而sleep可以在任何地方使用(使用范围)。4、sleep()方法必须捕获异常InterruptedException,而wait()、notify()和notifyAll()不需要捕获异常。注意:sleep方法只让出了CPU,而并不会释放同步资源锁。线程执行sleep()方法后会转入阻塞状态。sleep()方法指定的时间为线程不会运行的最短时间。因此,sleep()方法不能保证该线程睡眠到期后就开始执行。5.volatile是什么?可以保证有序性吗?volatile是java并发编程的一个关键字。volatile可以保证可见性,有序性(一定程度上),但不能保证原子性因为volatile关键字能禁止指令重排,所以一定程度上保证了有序性;volatile关键字禁止指令重排的两层意思:(1)当程序执行volatile关键字进行读操作或写操作时,volatile关键字前面所有程序操作已经全部完成且结果对后面的所有操作均显示,volatile关键字后面的操作已经还没有进行。(2)进行指令优化时,不能将volatile关键字后面的语句放在volatile关键字前面执行,也不能将volatile关键字前面的语句放在volatile关键字后面执行;可见性是指当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值有序性即程序执行的顺序按照代码的先后顺序执行原子性即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。volatile关键字的应用状态标记量单例模式中的double check指令重排序一般来说,处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。比如上面的代码中,语句1和语句2谁先执行对最终的程序结果并没有影响,那么就有可能在执行过程中,语句2先执行而语句1后执行。但是有依赖关系的语句不会进行重排序,如下面求圆面积的代码double pi = 4.14 //A double r = 1.0 //B double area = pi * r * r //c 程序的执行顺序只有下面这2个形式A->B->C和B->A->C,因为A和C之间存在依赖关系,同时B和C之间也存在依赖关系。因此最终执行的指令序列中C不能被重排序到A和B前面。6.Thread 类中的start()和run()方法有什么区别?start():使用start可以启动线程,创建一个新的线程,真正实现了多线程运行,在内部调用run方法且无需等待run方法体代码执行完毕而直接继续执行下面的代码。run():run()方法只是类的一个普通方法而已,使用run不会创建新线程,而是在之前的线程中执行了我们在run里面重写的内容。7.为什么wait, notify 和notifyAll这些方法不在thread类里面?Java提供的锁是对象级的而不是线程级的,每个对象都有锁,通过线程获得。简单的说,由于wait,notify,notifyAll都是锁级别的操作,所以把他们定义在object类中因为锁属于对象。8.为什么wait, notify方法要在同步块中调用?参考:面试官:为什么wait()方法要放在同步块中?9.Java中interrupted和isInterruptedd方法的区别?interruptinterrupt 方法用于中断线程。调用该方法的线程的状态为将被置为”中断”状态。注意:线程中断仅仅是置线程的中断状态位,不会停止线程。需要用户自己去监 视线程的状态为并做处理。支持线程中断的方法(也就是线程中断后会抛出 interruptedException 的方法)就是在监视线程的中断状态,一旦线程的中断状 态被置为“中断状态”,就会抛出中断异常。interrupted查询当前线程的中断状态,并且清除原状态。如果一个线程被中断了,第一次调 用 interrupted 则返回 true,第二次和后面的就返回 false 了。isInterrupted仅仅是查询当前线程的中断状态10.Java中synchronized和ReentrantLock有什么不同?用法不同:synchronized 可以用来修饰普通方法、静态方法和代码块,而 ReentrantLock 只能用于代码块(lock()和unlock()方法配合try/finally语句块来完成)。获取锁和释放锁的机制不同:synchronized 是自动加锁和释放锁的,而 ReentrantLock 需要手动加锁和释放锁。锁类型不同:synchronized 是非公平锁,而 ReentrantLock 默认为非公平锁,也可以手动指定为公平锁。响应中断不同:ReentrantLock 可以响应中断(catch InterruptedException),解决死锁的问题,而 synchronized 不能响应中断。底层实现不同:synchronized 是 JVM 层面通过监视器实现的,而 ReentrantLock 是基于 AQS 实现的。公平锁是指多个线程在等待同一个锁时,必须按照申请的时间顺序来依次获得锁;而非公平锁则不能保证这一点。非公平锁在锁被释放时,任何一个等待锁的线程都有机会获得锁。11.有三个线程T1, T2, T3,如何保证顺序执行?用线程类的join()方法在一个线程中启动另一个线程,另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2,T2调用T1),这样T1就会先完成而T3最后完成。12.SynchronizedMap 和 ConcurrentHashMap 有什么区别?SynchronizedMap 一次锁住整张表来保证线程安全,所以每次只能有一个线程来访问Map.ConcurrentHashMap 使用分段锁来保证在多线程下的性能。ConcurrentHashMap 中则是一次锁住一个桶。ConcurrentHashMap 默认将 hash 表分为 16 个桶,诸如get、put、remove 等常用操作只锁当前需要用到的桶。这样,原来只能一个线程进入,现在却能同时有 16 个写线程执行,并发性能的提升是显而易见的。另外 ConcurrrentHashMap 使用一种不同的迭代方式。在这种迭代方式中,当 iterator 被创建后集合再发生改变就不再是抛出 ConcurrentModificationException,取而代之的是在改变 new 新的数据从而不影响原有的数据,iterator 完成后再将头指针替换为新的数据,这样 iterator 线程可以使用原来老的数据,而写线程也可以并发的完成改变。13.Thread类中的yield方法有什么作用?yield 即 "谦让",也是 Thread 类的方法。它让掉当前线程 CPU 的时间片,使正在运行中的线程重新变成就绪状态,并重新竞争 CPU 的调度权。它可能会获取到,也有可能被其他线程获取到。yield 和 sleep 的异同1)yield, sleep 都能暂停当前线程,sleep 可以指定具体休眠的时间,而 yield 则依赖 CPU 的时间片划分。2)yield, sleep 两个在暂停过程中,如已经持有锁,则都不会释放锁资源。3)yield 不能被中断,而 sleep 则可以接受中断。14.Java线程池中submit()和execute()方法有什么区别?两个方法都可以向线程池提交任务,execute()方法的返回类型是 void,它定义在Executor 接口中。而 submit()方法可以返回持有计算结果的 Future 对象,它定义在ExecutorService 接口中,它扩展了 Executor 接口,其它线程池类像ThreadPoolExecutor 和 ScheduledThreadPoolExecutor 都有这些方法。15.volatile关键字的作用?volatile关键字的作用保证可见性;防止指令重排;16.常用的线程池有哪些?17.简述一下你对线程池的理解线程池本质上是一种池化技术,而池化技术是一种资源复用的思想。它的核心设计目标,我认为有两个:减少线程的频繁创建和销毁带来的性能开销,因为线程创建会涉及到CPU上下文切换、内存分配等工作。线程池本身会有参数来控制线程创建的数量,这样就可以避免无休止的创建线程带来的资源利用率过高的问题,起到了资源保护的作用。18.Runnable接口和Callable接口的区别Runnable需要实现run()方法Callable需要实现call()方法Runnable从jdk1.1开始加入Callable从jdk1.5开始加入区别1: 两者最大的区别,实现Callable接口的任务线程能返回执行结果,而实现Runnable接口的任务线程不能返回执行结果注意点:Callable接口支持返回执行结果,此时需要调用FutureTask.get()方法实现,此方法会阻塞线程直到获取“将来”的结果,当不调用此方法时,主线程不会阻塞区别2:Callable接口实现类中run()方法允许将异常向上抛出,也可以直接在内部处理(try…catch); 而Runnable接口实现类中run()方法的异常必须在内部处理掉,不能向上抛出参考资料Github上365道Java高频面试复习题,助你拿爆大厂offerJava 实现多线程的四种方式详解Java实现多线程的三种方式Java四种常用线程池的详细介绍notify() 和 notifyAll()方法的使用和区别并发关键字:volatile如何保证可见性和有序性?面试官:为什么wait()方法要放在同步块中?Java面试突击之synchronized和ReentrantLock有什么区别?多线程 Thread.yield 方法到底有什么用?5种常用的线程池【小明】谈谈你对线程池的理解【建议收藏】【Java面试】简述一下你对线程池的理解?
2022年09月07日
326 阅读
0 评论
0 点赞
2022-04-29
面试题:进程、线程及协程的区别
1.概念进程: 进程是一个具有一定独立功能的程序关于某个数据集合上的一次运行活动,是系统资源分配和独立运行的最小单位;线程: 线程是进程的一个执行单元,是任务调度和系统执行的最小单位;协程: 协程是一种用户态的轻量级线程,协程的调度完全由用户控制。2.进程与线程的区别1、根本区别: 进程是操作系统资源分配和独立运行的最小单位;线程是任务调度和系统执行的最小单位。2、地址空间区别: 每个进程都有独立的地址空间,一个进程崩溃不影响其它进程;一个进程中的多个线程共享该 进程的地址空间,一个线程的非法操作会使整个进程崩溃。3、上下文切换开销区别: 每个进程有独立的代码和数据空间,进程之间上下文切换开销较大;线程组共享代码和数据空间,线程之间切换的开销较小。3.进程与线程的联系一个进程由共享空间(包括堆、代码区、数据区、进程空间和打开的文件描述符)和一个或多个线程组成,各个线程之间共享进程的内存空间,而一个标准的线程由线程ID、程序计数器PC、寄存器和栈组成。进程和线程之间的联系如下图所示:4.进程与线程的选择1、线程的创建或销毁的代价比进程小,需要频繁创建和销毁时应优先选用线程;2、线程上下文切换的速度比进程快,需要大量计算时优先选用线程;3、线程在CPU上的使用效率更高,需要多核分布时优先选用线程,需要多机分布时优先选用进程4、线程的安全性、稳定性没有进程好,需要更稳定安全时优先使用进程。综上,线程创建和销毁的代价低、上下文切换速度快、对系统资源占用小、对CPU的使用效率高,因此一般情况下优先选择线程进行高并发编程;但线程组的所有线程共用一个进程的内存空间,安全稳定性相对较差,若其中一个线程发生崩溃,可能会使整个进程,因此对安全稳定性要求较高时,需要优先选择进程进行高并发编程。5.协程协程拥有自己的寄存器上下文和栈。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。因此,协程能保留上一次调用时的状态(即所有局部状态的一个特定组合),每次过程重入时,就相当于进入上一次调用的状态。这个过程完全由程序控制,不需要内核进行调度。协程与线程的关系如下图所示:6.协程与线程的区别1、根本区别: 协程是用户态的轻量级线程,不受内核调度;线程是任务调度和系统执行的最小单位,需要内核调度。2、运行机制区别: 线程和进程是同步机制,而协程是异步机制。3、上下文切换开销区别: 线程运行状态切换及上下文切换需要内核调度,会消耗系统资源;而协程完全由程序控制,状态切换及上下文切换不需要内核参与。参考资料进程、线程及协程的区别
2022年04月29日
832 阅读
1 评论
0 点赞
2022-04-05
JAVA判断一个数是否是质数/批量求质数
1.质数定义:只能被1或者自身整除的自然数(不包括1),称为质数。2.判断一个数是否是质数方法一:根据质数的定义求(效率最低)利用它的定义可以循环判断该数除以比它小的每个自然数(大于1),如果有能被它整除的,则它就不是质数。 时间复杂度:$O(n^2)$/** * 判断传入数值是否为素数 * @param num * @return flag */ private static boolean isPrime1(int num){ boolean flag=true; //判断是否为质数的标记 for (int j=num-1;j>1;j--){ //逐个除以[1,n]区间的数字 if (num%j==0){ flag = false; //若能整除,则置false标记为合数 } } return flag; }方法二:利用合数定理(原理和方法一一致,效率提高了)如果一个数是合数,那么它的最小质因数肯定小于等于他的平方根。 例如,20的最小质因数4必然小于20开方。 时间复杂度:$O(n^{\frac{1}{2}})$/** * 方法二 * @param num * @return */ private static boolean isPrime2(int num){ int s = (int) Math.sqrt(num); //求出数字n的开方 boolean flag = true; //判断是否为质数的标记 for (int j=2;j<=s;j++){ //逐个除以[1,n^1/2]区间的数字 if (num % j == 0){ flag = false; //若能整除,则置false标记为合数 } } return flag; }3.批量求质数--筛法求质数(效率最高,但会比较浪费内存)首先建立一个boolean类型的数组,用来存储你要判断某个范围内自然数中的质数.例:你要输出小于100的质数,可以建立一个大小为101(建立11个存储位置是为了让数组位置与其大小相同)的boolean数组,位置1置false,其他位置初始化为true。即数组初始状态 [null,false,true,true......]然后再获取数字n的开方,即10,然后再从2开始,2的倍数位置置false,即 4 6 8 10....,3的倍数位置置false即 6 9 12..........,以此类推,就把初始数列的合数位置值变成false,true值的位置则为素数。/** * 通过对数组进行标记后,再逐个判断 * @param num * @return */ private static boolean [] isPrime3(int num){ boolean [] array = new boolean[num+1]; //加1为了数组位置一致 array[1] = false; //false为合数,ture为质数 1不是质数 for (int i = 2;i < num;i++){ array[i] = true; //生成一个长度为n的布尔数列 [null,false,true........] } int s = (int)Math.sqrt(num); for (int i = 2;i<=s;i++){ if (array[i]){ //检查是否已经置fales(可以防止重复置false) for (int j=i;j*i<=num;j++){ //将i的倍数下标位置置为false array[j*i] = false; } } } return array; //标记好后的数组返回 }
2022年04月05日
1,215 阅读
7 评论
0 点赞
2022-04-05
DNS域名解析过程
1.简略版浏览器缓存——》系统hosts文件——》本地DNS解析器缓存——》本地域名服务器(本地配置区域资源、本地域名服务器缓存)——》根域名服务器——》主域名服务器——》下一级域名域名服务器 客户端——》本地域名服务器(递归查询) 本地域名服务器—》DNS服务器的交互查询是迭代查询2.详细解释当我们在浏览器地址栏中输入某个Web服务器的域名时。用户主机首先用户主机会首先在自己的DNS高速缓存中查找该域名所应的IP地址。如果没有找到,则会向网络中的某台DNS服务器查询,DNS服务器中有域名和IP地映射关系的数据库。当DNS服务器收到DNS查询报文后,在其数据库中查询,之后将查询结果发送给用户主机。现在,用户主机中的浏览器可以通过Web服务器的IP地址对其进行访问了。如果上级的DNS没有该域名的DNS缓存,则会继续向更上级查询,包含两种查询方式,分别是递归查询和迭代查询。递归查询如果主机所询问的本地域名服务器不知道被查询域名的 IP 地址,那么本地域名服务器就以 DNS 客户端的身份,向其他根域名服务器继续发出查询请求报文,即替主机继续查询,而不是让主机自己进行下一步查询。我们以一个例子来了解DNS递归查询的工作原理,假设图中的主机 (IP地址为m.xyz.com) 想知道域名y.abc.com的IP地址。1、主机首先向其本地域名服务器进行递归查询。2、本地域名服务器收到递归查询的委托后,也采用递归查询的方式向某个根域名服务器查询。3、根域名服务器收到递归查询的委托后,也采用递归查询的方式向某个顶级域名服务器查询。4、顶级域名服务器收到递归查询的委托后,也采用递归查询的方式向某个权限域名服务器查询。过程如图所示:迭代查询当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP 地址,要么告诉本地服务器下一步应该找哪个域名服务器进行查询,然后让本地服务器进行后续的查询。迭代查询过程如下:1、主机首先向其本地域名服务器进行递归查询。2、本地域名服务器采用迭代查询,它先向某个根域名服务器查询。3、根域名服务器告诉本地域名服务器,下一次应查询的顶级域名服务器的IP地址。4、本地域名服务器向顶级域名服务器进行迭代查询。5、顶级域名服务器告诉本地域名服务器,下一次应查询的权限域名服务器的IP地址。6、本地域名服务器向权限域名服务器进行迭代查询。7、权限域名服务器告诉本地域名服务器所查询的域名的IP地址。8、本地域名服务器最后把查询的结果告诉主机。过程如图所示:由于递归查询对于被查询的域名服务器负担太大,通常采用以下模式:从请求主机到本地域名服务器的查询是递归查询,而其余的查询是迭代查询。参考资料DNS解析过程及原理DNS域名解析过程多张图带你彻底搞懂DNS域名解析过程
2022年04月05日
445 阅读
0 评论
0 点赞
2022-04-05
麒麟软件面试题整理
1.“equal”、“==” 和 “hashCode” 的区别和使用场景三者都是用于判断对象之间是否相等的,判断的方式不一样。==对于基本类型,==是比较其值是不是相等,对于引用类型,==比较两个对象是否相同。equals方法equals 方法是用于比较两个独立对象的内容是否相同:如果一个类没有重写 equals(Object obj)方法,则等价于通过 == 比较两个对象,即比较的是对象在内存中的空间地址是否相等。如果重写了equals(Object ibj)方法,则根据重写的方法内容去比较相等,返回 true 则相等,false 则不相等。hashcode()hashCode 是用来在散列存储结构中确定对象的存储地址的。hashCode 的存在主要用于查找的快捷性,如 Hashtable, HashMap 等,如果两个对象相同,就是适用于 euqals(java.lang.Object) 方法,那么这两个对象的 hashCode一定相同。如果对象的euqals 方法被重写,那么对象的 hashCode 也尽量重写,并且产生 hashCode 使用的对象,一定要和 equals 方法中使用的一致。两个对象的 hashCode 相同,并不一定表示这两个对象就相同,也就是不一定适用于equals() 方法,只能够说明这两个对象在散列存储结构中,如 Hashtable.,他们存在同一个篮子里。使用场景:equals 方法是用于比较两个独立对象的内容是否相同。hashCode方法存在的主要目的就是提高判断效率,如用来在散列存储结构中确定对象的存储地址的。因为 hashCode 并不是完全可靠,有时候不同的对象他们生成的 hashcode 也会一样(hash冲突),所以 hashCode只能说是大部分时候可靠,并不是绝对可靠,所以可以得出:equals 相等的两个对象他们的 hashCode 肯定相等,也就是用 equals 对比是绝对可靠的hashCode 相等的两个对象他们的 equals 不一定相等,也就是 hashCode 不是绝对可靠的。对于需要大量并且快速的对比的话如果都用 equals 去做显然效率太低,解决方式是,每当需要对比的时候,首先用 hashCode 去对比,如果 hashCode 不一样,则表示这两个对象肯定不相等(也就是不必再用 equals 去再对比了),如果 hashCode 相同,此时再对比他们的 equals,如果 equals 也相同,则表示这两个对象是真的相同了2.DNS域名解析过程简略版浏览器缓存——》系统hosts文件——》本地DNS解析器缓存——》本地域名服务器(本地配置区域资源、本地域名服务器缓存)——》根域名服务器——》主域名服务器——》下一级域名域名服务器 客户端——》本地域名服务器(递归查询) 本地域名服务器—》DNS服务器的交互查询是迭代查询详细解释当我们在浏览器地址栏中输入某个Web服务器的域名时。用户主机首先用户主机会首先在自己的DNS高速缓存中查找该域名所应的IP地址。如果没有找到,则会向网络中的某台DNS服务器查询,DNS服务器中有域名和IP地映射关系的数据库。当DNS服务器收到DNS查询报文后,在其数据库中查询,之后将查询结果发送给用户主机。现在,用户主机中的浏览器可以通过Web服务器的IP地址对其进行访问了。如果上级的DNS没有该域名的DNS缓存,则会继续向更上级查询,包含两种查询方式,分别是递归查询和迭代查询。递归查询如果主机所询问的本地域名服务器不知道被查询域名的 IP 地址,那么本地域名服务器就以 DNS 客户端的身份,向其他根域名服务器继续发出查询请求报文,即替主机继续查询,而不是让主机自己进行下一步查询。我们以一个例子来了解DNS递归查询的工作原理,假设图中的主机 (IP地址为m.xyz.com) 想知道域名y.abc.com的IP地址。1、主机首先向其本地域名服务器进行递归查询。2、本地域名服务器收到递归查询的委托后,也采用递归查询的方式向某个根域名服务器查询。3、根域名服务器收到递归查询的委托后,也采用递归查询的方式向某个顶级域名服务器查询。4、顶级域名服务器收到递归查询的委托后,也采用递归查询的方式向某个权限域名服务器查询。过程如图所示:迭代查询当根域名服务器收到本地域名服务器发出的迭代查询请求报文时,要么给出所要查询的IP 地址,要么告诉本地服务器下一步应该找哪个域名服务器进行查询,然后让本地服务器进行后续的查询。迭代查询过程如下:1、主机首先向其本地域名服务器进行递归查询。2、本地域名服务器采用迭代查询,它先向某个根域名服务器查询。3、根域名服务器告诉本地域名服务器,下一次应查询的顶级域名服务器的IP地址。4、本地域名服务器向顶级域名服务器进行迭代查询。5、顶级域名服务器告诉本地域名服务器,下一次应查询的权限域名服务器的IP地址。6、本地域名服务器向权限域名服务器进行迭代查询。7、权限域名服务器告诉本地域名服务器所查询的域名的IP地址。8、本地域名服务器最后把查询的结果告诉主机。过程如图所示:由于递归查询对于被查询的域名服务器负担太大,通常采用以下模式:从请求主机到本地域名服务器的查询是递归查询,而其余的查询是迭代查询。3.synchronized关键字及加到方法和对象上的区别Synchronized是Java提供的同步关键字,在多线程场景下,对共享资源代码段进行读写操作(必须包含写操作,光读不会有线程安全问题,因为读操作天然具备线程安全特性),可能会出现线程安全问题,我们可以使用Synchronized锁定共享资源代码段,达到互斥(mutualexclusion)效果,保证线程安全。synchronized 既可以加在一段代码上,也可以加在方法上。加到对象上(加到代码块上)使用synchronized(object) { 代码块.... } 能对代码块进行加锁,不允许其他线程访问,其的作用原理是:在object内有一个变量,当有线程进入时,判断是否为0,如果为0,表示可进入执行该段代码,同时将该变量设置为1,这时其他线程就不能进入;当执行完这段代码时,再将变量设置为0。加到方法上synchronized加在方法上本质上还是等价于加在对象上的。如果synchronized加在一个类的普通方法上,那么相当于synchronized(this)。如果synchronized加载一个类的静态方法上,那么相当于synchronized(Class.this)。4.HTTP/HTTPS区别HTTP协议传输的数据都是未加密的,也就是明文的,因此使用HTTP协议传输隐私信息非常不安全,为了保证这些隐私数据能加密传输,于是网景公司设计了SSL(Secure Sockets Layer)协议用于对HTTP协议传输的数据进行加密,从而就诞生了HTTPS。为了数据传输的安全,HTTPS在HTTP的基础上加入了SSL/TLS协议,SSL/TLS依靠证书来验证服务器的身份,并为浏览器和服务器之间的通信加密。HTTPS和HTTP的区别主要如下:1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用。2、http的信息是明文传输,https则是具有安全性的ssl加密传输协议。3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。5.MySQL删除数据的方式都有哪些?/delete/drop/truncate区别、谁的速度更快以及原因可以这么理解,一本书,delete是把目录撕了,truncate是把书的内容撕下来烧了,drop是把书烧了常用的三种删除方式:通过 delete、truncate、drop 关键字进行删除;这三种都可以用来删除数据,但场景不同。执行速度drop > truncate >> DELETE区别详解delete1、DELETE属于数据库DML操作语言,只删除数据不删除表的结构,会走事务,执行时会触发trigger;2、在 InnoDB 中,DELETE其实并不会真的把数据删除,mysql 实际上只是给删除的数据打了个标记为已删除,因此 delete 删除表中的数据时,表文件在磁盘上所占空间不会变小,存储空间不会被释放,只是把删除的数据行设置为不可见。虽然未释放磁盘空间,但是下次插入数据的时候,仍然可以重用这部分空间(重用 → 覆盖)。3、DELETE执行时,会先将所删除数据缓存到rollback segement中,事务commit之后生效;4、delete from table_name删除表的全部数据,对于MyISAM 会立刻释放磁盘空间,InnoDB 不会释放磁盘空间;5、对于delete from table_name where xxx 带条件的删除, 不管是InnoDB还是MyISAM都不会释放磁盘空间;6、delete操作以后使用optimize table table_name会立刻释放磁盘空间。不管是InnoDB还是MyISAM 。所以要想达到释放磁盘空间的目的,delete以后执行optimize table 操作。7、delete 操作是一行一行执行删除的,并且同时将该行的的删除操作日志记录在redo和undo表空间中以便进行回滚(rollback)和重做操作,生成的大量日志也会占用磁盘空间。truncate1、truncate:属于数据库DDL定义语言,不走事务,原数据不放到 rollback segment 中,操作不触发 trigger。执行后立即生效,无法找回 执行后立即生效,无法找回 执行后立即生效,无法找回2、truncate table table_name立刻释放磁盘空间 ,不管是 InnoDB和MyISAM。truncate table其实有点类似于drop table 然后creat,只不过这个create table 的过程做了优化,比如表结构文件之前已经有了等等。所以速度上应该是接近drop table的速度;3、truncate能够快速清空一个表。并且重置auto_increment的值。小心使用 truncate,尤其没有备份的时候,如果误删除线上的表,记得及时联系中国民航,订票电话:400-806-9553Drop1、drop:属于数据库DDL定义语言,同Truncate;2、drop table table_name 立刻释放磁盘空间 ,不管是 InnoDB 和 MyISAM;drop 语句将删除表的结构被依赖的约束(constrain)、触发器(trigger)、索引(index); 依赖于该表的存储过程/函数将保留,但是变为 invalid 状态。小心使用 drop ,要删表跑路的兄弟,请在订票成功后在执行操作!订票电话:400-806-95536.Linux根目录下有哪些基本目录/bin:里边包含了一般程序工具,用户、管理员、系统都可以调用。比如常用的ls、cp、cat、mv等等。 /sbin:系统和系统管理员用到的程序工具。 /lib、/lib64:库文件,包含了所有系统和用户需要的程序文件,64表示64位,但实际上除特殊的库,大部分还是链接到了lib目录下。 /home:一般用户目录,一般一个用户对应一个目录,保存用户的数据。 /root:root用户的家目录。 /etc:包含了大部分重要的系统配置文件,这里文件的作用类似windows中的控制面板。 /boot:系统启动文件和内核,在有些发行版中还包括grub,grub是一种通用的启动引导程序。 /dev:系统设备文件目录,除cpu外的所有的硬件设备都会抽象成特殊的文件放在这里,虚拟设备也放在这里。 /media:磁盘设备自动挂载的位置。按照用户分类,每一个用户目录下有其磁盘目录。 /cdrom:专门用来挂载光盘的目录,有些发行版将该目录放在media或mnt目录下。 /mnt:标准挂载点,可以挂载外设磁盘。 /opt:一般存放第三方软件。 /tmp:系统使用的临时空间,重启后会清空。 /var:包含一些用户可变的或临时的文件,比如log文件、邮件队列、网络下载的临时文件等等。 /sys:与proc类似的虚拟文件系统,都是内核提供给用户的接口,可读可写。 /proc:包含系统资源信息的虚拟文件系统,提供了一个接触内核数据的接口,大部分是只读的,有些允许改变。系统运行时才有文件。7.Java类加载过程Java类加载过程主要可以分为三个步骤:加载、连接、初始化。加载:是Java将字节码数据从不同的数据源读取到JVM中,映射为JVM认可的数据结构。连接:是把原始的类定义信息平滑地转入JVM运行的过程中。这一阶段可以细分为验证、准备、解析三步。验证:1.格式检查 --> 魔数验证、版本检查、长度检查2.语义检查 --> 是否继承final、是否有父类、是否实现抽象方法3.直接验证 --> 跳转指令是否只想正确的位置,操作数类型是否合理4.符号引用验证 --> 符号引用的直接引用是否存在准备:为类中的所有静态变量分配内存空间,并为其设置一个初始值(由于还没有产生对象,实例变量不在此操作范围内)被final修饰的静态变量, 会直接赋予原值;类字段的字段属性表中存在ConstantValue属性,则在准备阶段,其值就是ConstantValue的值解析:将常量池中的符号引用转为直接引用(得到类或者字段、方法在内存中的指针或者偏移量,以便直接调用该方法),这个可以在初始化之后 再执行。可以认为是一些静态绑定的会被解析,动态绑定则只会在运行是进行解析;静态绑定包括一些final方法(不可以重写),static方法(只 会属于当前类),构造器(不会被重写)初始化:是执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑。8.堆和栈的区别堆和栈的区别主要有五大点,分别是:1、申请方式的不同。栈由系统自动分配,而堆是人为申请开辟;2、申请大小的不同。栈获得的空间较小,而堆获得的空间较大;3、申请效率的不同。栈由系统自动分配,速度较快,而堆一般速度比较慢;4、存储内容的不同。栈内存存储的是局部变量而堆内存存储的是实体; 栈在函数调用时,函数调用语句的下一条可执行语句的地址第一个进栈,然后函数的各个参数进栈,其中静态变量是不入栈的。而堆一般是在头部用一个字节存放堆的大小,堆中的具体内容是人为安排;5、底层不同。栈是连续的空间,而堆是不连续的空间。6、栈内存存放的变量生命周期一旦结束就会被释放,而堆内存存放的实体会被垃圾回收机制不定时的回收。9.生产者消费者模型概念生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。321原则三种角色:生产者、消费者、仓库两种关系:生产者与生产者之间是互斥关系,消费者与消费者之间是互斥关系,生产者与消费者之间是同步与互斥关系。一个交易场所:仓库优点解耦–生产者。消费者之间不直接通信,降低了耦合度。支持并发支持忙闲不均PV原语描述s1初始值为缓冲区大小、s2初始值为0 生产者: 生产一个产品; P(s1); 送产品到缓冲区; V(s2); 消费者: P(s2); 从缓冲区取出产品; V(s1); 消费水平;代码实现synchronized + wait() + notify() 方式package ProducerAndConsumer; import java.util.ArrayList; import java.util.List; class Produce extends Thread { List<Object> productBuffer; // 产品缓冲区 int bufferCapacity; // 缓冲区容量 public Produce(int bufferCapacity,List<Object> productBuffer){ this.productBuffer = productBuffer; this.bufferCapacity = bufferCapacity; } @Override public void run() { while (true){ // 生产行为 synchronized (productBuffer){ // 如果某一个生产者能执行进来,说明此线程具有productBuffer对象的控制权,其它线程(生产者&消费者)都必须等待 if(productBuffer.size()==bufferCapacity){ // 缓冲区满了 try { productBuffer.wait(1); // 释放控制权并等待 } catch (InterruptedException e) { e.printStackTrace(); } }else { // 缓冲区没满可以继续生产 productBuffer.add(new Object()); System.out.println("生产者生产了1件物品,当前缓冲区里还有" + productBuffer.size() + "件物品"); productBuffer.notifyAll(); // 唤醒等待队列中所有线程 } } // 模拟生产缓冲时间 try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer extends Thread{ List<Object> productBuffer; // 产品缓冲区 int bufferCapacity; // 缓冲区容量 public Consumer(int bufferCapacity,List<Object> productBuffer){ this.productBuffer = productBuffer; this.bufferCapacity = bufferCapacity; } @Override public void run(){ while (true){//消费行为 synchronized (productBuffer){ if(productBuffer.isEmpty()){ //产品缓冲区为空,不能消费,只能等待 try { productBuffer.wait(1); } catch (InterruptedException e) { e.printStackTrace(); } }else { // 缓冲区没空可以继续消费 productBuffer.remove(0); System.out.println("消费者消费了1个物品,当前缓冲区里还有" + productBuffer.size() + "件物品"); productBuffer.notifyAll(); } } // 模拟消费缓冲时间 try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ProducerAndConsumer { public static void main(String[] args) { List<Object> productBuffer = new ArrayList<>(); // 产品缓冲区 int bufferCapacity = 3; // 缓冲区容量 for (int i = 0; i < 3; i++) { new Produce(bufferCapacity,productBuffer).start(); } for (int i = 0; i < 3; i++) { new Consumer(bufferCapacity,productBuffer).start(); } } }10.servlet是线程安全的吗Servlet不是线程安全的。当Tomcat接收到Client的HTTP请求时,Tomcat从线程池中取出一个线程,之后找到该请求对应的Servlet对象并进行初始化,之后调用service()方法。要注意的是每一个Servlet对象再Tomcat容器中只有一个实例对象,即是单例模式。如果多个HTTP请求请求的是同一个Servlet,那么着两个HTTP请求对应的线程将并发调用Servlet的service()方法。11.http请求头和响应头信息请求头信息Accept:浏览器能够处理的内容类型Accept-Charset:浏览器能够显示的字符集Accept-Encoding:浏览器能够处理的压缩编码Accept-Language:浏览器当前设置的语言Connection:浏览器与服务器之间连接的类型Cookie:当前页面设置的任何CookieHost:发出请求的页面所在的域Referer:发出请求的页面的URLUser-Agent:浏览器的用户代理字符串响应头信息Date:表示消息发送的时间,时间的描述格式由rfc822定义server:服务器名字。Connection:浏览器与服务器之间连接的类型content-type:表示后面的文档属于什么MIME类型Cache-Control:控制HTTP缓存参考资料“equal”、“==” 和 “hashCode” 的区别和使用场景?Java 基础 | hashCode 和 equals 在实体类的应用场景 Java的synchronized加在方法上或者对象上有什么区别?13张图,深入理解Synchronized全网最全JAVA面试八股文,终于整理完了面试官灵魂一问:MySQL 的 delete、truncate、drop 有什么区别?Linux根目录下各个目录的介绍HTTP与HTTPS的区别多张图带你彻底搞懂DNS域名解析过程java 类加载过程(步骤)详解堆和栈的区别有哪些?PV操作-生产者/消费者关系Java 学习笔记 使用synchronized实现生产者消费者模式 经典面试题 -- 手写生产者消费者模式Java面试题:Servlet是线程安全的吗?http的请求头都有那些信息
2022年04月05日
793 阅读
0 评论
0 点赞
2022-04-05
生产者消费者模型原理及java实现
1.概念生产者消费者模式就是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。这个阻塞队列就是用来给生产者和消费者解耦的。2.321原则三种角色:生产者、消费者、仓库两种关系:生产者与生产者之间是互斥关系,消费者与消费者之间是互斥关系,生产者与消费者之间是同步与互斥关系。一个交易场所:仓库3.优点解耦–生产者。消费者之间不直接通信,降低了耦合度。支持并发支持忙闲不均4.PV原语描述s1初始值为缓冲区大小、s2初始值为0 生产者: 生产一个产品; P(s1); 送产品到缓冲区; V(s2); 消费者: P(s2); 从缓冲区取出产品; V(s2); 消费水平;5.代码实现5.1 synchronized + wait() + notify() 方式package ProducerAndConsumer; import java.util.ArrayList; import java.util.List; class Produce extends Thread { List<Object> productBuffer; // 产品缓冲区 int bufferCapacity; // 缓冲区容量 public Produce(int bufferCapacity,List<Object> productBuffer){ this.productBuffer = productBuffer; this.bufferCapacity = bufferCapacity; } @Override public void run() { while (true){ // 生产行为 synchronized (productBuffer){ // 如果某一个生产者能执行进来,说明此线程具有productBuffer对象的控制权,其它线程(生产者&消费者)都必须等待 if(productBuffer.size()==bufferCapacity){ // 缓冲区满了 try { productBuffer.wait(1); // 释放控制权并等待 } catch (InterruptedException e) { e.printStackTrace(); } }else { // 缓冲区没满可以继续生产 productBuffer.add(new Object()); System.out.println("生产者生产了1件物品,当前缓冲区里还有" + productBuffer.size() + "件物品"); productBuffer.notifyAll(); // 唤醒等待队列中所有线程 } } // 模拟生产缓冲时间 try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer extends Thread{ List<Object> productBuffer; // 产品缓冲区 int bufferCapacity; // 缓冲区容量 public Consumer(int bufferCapacity,List<Object> productBuffer){ this.productBuffer = productBuffer; this.bufferCapacity = bufferCapacity; } @Override public void run(){ while (true){//消费行为 synchronized (productBuffer){ if(productBuffer.isEmpty()){ //产品缓冲区为空,不能消费,只能等待 try { productBuffer.wait(1); } catch (InterruptedException e) { e.printStackTrace(); } }else { // 缓冲区没空可以继续消费 productBuffer.remove(0); System.out.println("消费者消费了1个物品,当前缓冲区里还有" + productBuffer.size() + "件物品"); productBuffer.notifyAll(); } } // 模拟消费缓冲时间 try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ProducerAndConsumer { public static void main(String[] args) { List<Object> productBuffer = new ArrayList<>(); // 产品缓冲区 int bufferCapacity = 3; // 缓冲区容量 for (int i = 0; i < 3; i++) { new Produce(bufferCapacity,productBuffer).start(); } for (int i = 0; i < 3; i++) { new Consumer(bufferCapacity,productBuffer).start(); } } }5.2 可重入锁ReentrantLock (配合Condition)方式package ProducerAndConsumer; import java.util.ArrayList; import java.util.List; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.ReentrantLock; class Produce extends Thread { List<Object> productBuffer; // 产品缓冲区 int bufferCapacity; // 缓冲区容量 ReentrantLock lock; // 可重入锁 Condition producerCondition; // 生产者condition Condition consumerCondition; // 消费者condition public Produce(int bufferCapacity,List<Object> productBuffer,ReentrantLock lock,Condition producerCondition,Condition consumerCondition){ this.productBuffer = productBuffer; this.bufferCapacity = bufferCapacity; this.lock = lock; this.producerCondition = producerCondition; this.consumerCondition = consumerCondition; } @Override public void run() { while (true) { // 生产行为 lock.lock(); //加锁 if (productBuffer.size() == bufferCapacity) { // 缓冲区满了 try { producerCondition.await(); // 释放控制权并等待 } catch (InterruptedException e) { e.printStackTrace(); } } else { // 缓冲区没满可以继续生产 productBuffer.add(new Object()); System.out.println("生产者生产了1件物品,当前缓冲区里还有" + productBuffer.size() + "件物品"); consumerCondition.signal(); // 唤醒消费者线程 } lock.unlock(); //解锁 // 模拟生产缓冲时间 try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } } class Consumer extends Thread{ List<Object> productBuffer; // 产品缓冲区 int bufferCapacity; // 缓冲区容量 ReentrantLock lock; // 可重入锁 Condition producerCondition; // 生产者condition Condition consumerCondition; // 消费者condition public Consumer(int bufferCapacity,List<Object> productBuffer,ReentrantLock lock,Condition producerCondition,Condition consumerCondition){ this.productBuffer = productBuffer; this.bufferCapacity = bufferCapacity; this.lock = lock; this.producerCondition = producerCondition; this.consumerCondition = consumerCondition; } @Override public void run(){ while (true){//消费行为 lock.lock();//加锁 if(productBuffer.isEmpty()){ //产品缓冲区为空,不能消费,只能等待 try { consumerCondition.await(); } catch (InterruptedException e) { e.printStackTrace(); } }else { // 缓冲区没空可以继续消费 productBuffer.remove(0); System.out.println("消费者消费了1个物品,当前缓冲区里还有" + productBuffer.size() + "件物品"); producerCondition.signal(); // 唤醒生产者线程继续生产 } lock.unlock(); //解锁 // 模拟消费缓冲时间 try { Thread.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } } } } public class ProducerAndConsumer { public static void main(String[] args) { List<Object> productBuffer = new ArrayList<>(); // 产品缓冲区 int bufferCapacity = 3; // 缓冲区容量 ReentrantLock lock = new ReentrantLock(); // 可重入锁 Condition producerCondition = lock.newCondition(); // 生产者condition Condition consumerCondition = lock.newCondition(); // 消费者condition for (int i = 0; i < 3; i++) { new Produce(bufferCapacity,productBuffer,lock,producerCondition,consumerCondition).start(); } for (int i = 0; i < 3; i++) { new Consumer(bufferCapacity,productBuffer,lock,producerCondition,consumerCondition).start(); } } }参考资料PV操作-生产者/消费者关系Java 学习笔记 使用synchronized实现生产者消费者模式 经典面试题 -- 手写生产者消费者模式
2022年04月05日
672 阅读
0 评论
0 点赞