面试攻略一(JAVA基础专题)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28202661/article/details/81782963

 

同步异步阻塞非阻塞

同步与异步

  实际上同步与异步是针对应用程序与内核的交互而言的。同步过程中进程触发IO操作并等待或者轮询的去查看IO操作是否完成。异步过程中进程触发IO操作以后,直接返回,做自己的事情,IO交给内核来处理,完成后内核通知进程IO完成。同步与异步如下图所示:

阻塞与非阻塞

  简单理解为需要做一件事能不能立即得到返回应答,如果不能立即获得返回,需要等待,那就阻塞了,否则就可以理解为非阻塞。

什么是Servlet

Java Servlet 是运行在 Web 服务器或应用服务器上的程序,它是作为来自 Web 浏览器或其他 HTTP 客户端的请求和 HTTP 服务器上的数据库或应用程序之间的中间层。

重点理解:Servlet 编写过滤器

Servlet 和 Filter 的比较

  • servlet:servlet是一种运行服务器端的java应用程序,具有独立于平台和协议的特性,并且可以动态的生成web页面,它工作在客户端请求与服务器响应的中间层。
  • filter:filter是一个可以复用的代码片段,可以用来转换HTTP请求、响应和头信息。Filter不像Servlet,它不能产生一个请求或者响应,它只是修改对某一资源的请求,或者修改从某一的响应。

详解:filter与servlet的比较(概念 、 生命周期 、职责 、执行过程)

什么是中间件

中间件是一类独立的系统软件或服务程序,分布式应用软件借助这种软件在不同的技术之间共享资源。中间件位于客户机/服务器的操作系统之上,管理计算机资源和网络通信。

常见的有如下几种:服务中间件、集成中间件、数据中间件、消息中间件、安全中间件。

用Java实现的中间件,统称Java中间件。中间件,可以理解为类库,介于类库和产品之间。

说简单一点,就是你的程序A和程序B互相通信使用的协议,程序A,B可以由不同语言不同平台构建。但是协议可以保证他们能互相认识互发的东西。

举例:
1 , RMI ( Remote Method Invocations, 远程调用)
2 , Load Balancing( 负载均衡,将访问负荷分散到各个服务器中 )
3 , Transparent Fail-over( 透明的故障切换 )
4 , Clustering( 集群 , 用多个小的服务器代替大型机)
5 , Back-end-Integration( 后端集成,用现有的、新开发的系统如何去集成遗留的系统 )
6 , T ransaction 事务(全局 / 局部)全局事务(分布式事务)局部事务(在同一数据库联 接
内的事务)
7 , Dynamic Redeployment ( 动态重新部署 , 在不停止原系统的情况下,部署新的系统)
8 , System Management( 系统管理 )
9 , Threading( 多线程处理 )
10 , Message-oriented Middleware 面向消息的中间件(异步的调用编程)
11 , Component Life Cycle( 组件的生命周期管理 )
12 , Resource pooling (资源池)
13 , Security (安全)
14 , Caching (缓存)

什么是反射

定义:

用一句话来解释反射的定义:自控制,自描述。即通过反射可以动态的获取类、属性、方法的信息,也能构造对象并控制对象的属性和行为。

应用:

JDBC的驱动加载方式是通过反射机制实现的,JDBC只是设计了驱动需要实现的接口,并不关心驱动厂商的个数和实现方式,只要安装统一的规范即可,至于类型的判断和具体方法的触发,交给运行期动态判断即可,这种反射机制的使用淋漓尽致的体现了多态,并且降低了类与类之间的耦合度。

什么是面向对象

OOP , Object-Oriented Programming ,面向对象编程不同于面向过程编程:
1 ) OOP 关注对象和角色,也就是事物的本质
2) OOP 把客观世界中的对象抽象成对应的类;
3 )通过类构造实例;
4)通过依赖、继承、实现等形式建立对象间的通信关系

优点:
( 2 ) OOP 易于扩展,增加或改变业务的功能,无需大幅改动改变源代码
( 3 ) OOP 易于建模, OOP 就是软件架构师在计算机高级语言中对客观世界的抽象和
再现,

线程与线程池

附:JAVA多线程编程

POJO

简单的Java对象(Plain Ordinary Java Objects)实际就是普通JavaBeans

 POJO有一些private的参数作为对象的属性。然后针对每个参数定义了get和set方法作为访问的接口。例如:

public class User {
  private long id;
  private String name;
  public void setId(long id) {
  this.id = id;
  }
  public void setName(String name) {
  this.name=name;
  }
  public long getId() {
  return id;
  }
  public String getName() {
  return name;
  }
  }

POJO对象有时也被称为Data对象,大量应用于表现现实中的对象。

简述cookies和session的区别

  1. cookies:是针对每一个网站的信息,每一个网站只对应一个,其它网站不能访问,这个文件是保存在客户端的,每次你打相应网站,浏览器会查找这个网站的cookies,如果有就会将这个文件起发送出去。cookies文件的内容大致包函这些信息如用户名,密码,设置等,也可以是web服务器按照一定算法产生的只有Web服务器可以理解的数据,这些数据发送给客户端,客户端带着这些数据访问该网站才能被该网站识别。
  2. 从session: 是针对每一个用户的,只有客户机访问,程序就会为这个客户新增一个session。session里主要保存的是用户的登录信息,操作信息等。这个session在用户访问结束后会被自动消失(如果超时也会)。

简述Servlet与JSP的关系

        最重要的一点就是 JSP就是servlet ,  jsp继承了servlet,JSP与Servlet主要有两方面的不同:编译:JSP修改后可以立即看到结果,不需要编译;而Servelt缺需要编译。转换:JSP是动态网页开发技术,是运行在服务器端的脚本语言,而Servlet是web服务器端编程技术。所以JSP运行时就是转换为Servlet,也就是java程序来执行。

作用范围:application > session > request > page

application所有用户,项目的所有页面
session:项目的所有页面(仅限于用户的一次登录) 
request:仅限于用户的一次登录,只适用于一次请求,数据只能两个页面传输
page数据只在本页面可用

JSP内置对象有哪些,各自起到的作用

1、HttpServletRequest的 request对象
作用:代表请求对象,用来接收客户端通过http协议连接传输到服务器端的数据。

2、HttpServletResponse的response对象
作用:代表响应对象,用来向客户端发送数据。

3、JspWriter的 out 对象
作用:主要用于向客户端发送数据。其中JspWriter是out的基类。

4、HttpSession 的session 对象
作用:主要用于来分别保存每个用户的个人信息,与请求关联的对话。会话状态的维持是每个web应用开发者都必须面对的问题。

5、ServletContext的application对象
作用:主要用于保存用户信息,代码片断的运行环境。它是一个共享的内置对象。即一个容器中多个用户共享一个application对象,故其保存的信息被所有的用户所共享。

6、PageContext的PageContext对象
作用:管理网页的属性,为jsp页面包装页面的上下文,管理对属于jsp中特殊可见部分中已经命名对象的访问。它的创建和初始化的工作都是由容器来自动完成的。

7、ServletConfig的Config对象
作用:代码片断配置对象,表示对servlet的配置。

8、Object 的page(相当于this) 对象
作用:处理jsp网页,是object类的一个实例。即它也是jsp的本身,只有在jsp的页面范围之内它才是合法的。

9、Exception
作用:处理jsp页面执行时,发生的错误和异常。

常用的设计模式(伪代码),并简述应用场景

七大设计原则:

1、单一职责原则【SINGLE RESPONSIBILITY PRINCIPLE】:一个类负责一项职责.
2、里氏替换原则【LISKOV SUBSTITUTION PRINCIPLE】:继承与派生的规则.
3、依赖倒置原则【DEPENDENCE INVERSION PRINCIPLE】:高层模块不应该依赖低层模块,二者都应该依赖其抽象;抽象不应该依赖细节;细节应该依赖抽象。即针对接口编程,不要针对实现编程.
4、接口隔离原则【INTERFACE SEGREGATION PRINCIPLE】:建立单一接口,不要建立庞大臃肿的接口,尽量细化接口,接口中的方法尽量少.
5、迪米特法则【LOW OF DEMETER】:低耦合,高内聚.
6、开闭原则【OPEN CLOSE PRINCIPLE】:一个软件实体如类、模块和函数应该对扩展开放,对修改关闭.
7、组合/聚合复用原则【Composition/Aggregation Reuse Principle(CARP) 】:尽量使用组合和聚合少使用继承的关系来达到复用的原则.

附:Java23种设计模式

附:Java23种设计模式(经典)

HashMap实现原理及源码分析

HashMap实现原理及源码分析

HashMap和HashTable区别

HashTable

  • 底层数组+链表实现,无论key还是value都不能为null,线程安全,实现线程安全的方式是在修改数据时锁住整个HashTable,效率低,ConcurrentHashMap做了相关优化
  • 初始size为11,扩容:newsize = olesize*2+1
  • 计算index的方法:index = (hash & 0x7FFFFFFF) % tab.length

HashMap

  • 底层数组+链表实现,可以存储null键和null值,线程不安全
  • 初始size为16,扩容:newsize = oldsize*2,size一定为2的n次幂
  • 扩容针对整个Map,每次扩容时,原来数组中的元素依次重新计算存放位置,并重新插入
  • 插入元素后才判断该不该扩容,有可能无效扩容(插入后如果扩容,如果没有再次插入,就会产生无效扩容)
  • 当Map中元素总数超过Entry数组的75%,触发扩容操作,为了减少链表长度,元素分配更均匀
  • 计算index方法:index = hash & (tab.length – 1)

HashSet和TreeSet的区别

HashSet
HashSet有以下特点
 不能保证元素的排列顺序,顺序有可能发生变化
 不是同步的
 集合元素可以是null,但只能放入一个null
当向HashSet集合中存入一个元素时,HashSet会调用该对象的hashCode()方法来得到该对象的hashCode值,然后根据 hashCode值来决定该对象在HashSet中存储位置。
简单的说,HashSet集合判断两个元素相等的标准是两个对象通过equals方法比较相等,并且两个对象的hashCode()方法返回值相 等
注意,如果要把一个对象放入HashSet中,重写该对象对应类的equals方法,也应该重写其hashCode()方法。其规则是如果两个对 象通过equals方法比较返回true时,其hashCode也应该相同。另外,对象中用作equals比较标准的属性,都应该用来计算 hashCode的值。

TreeSet类
TreeSet是SortedSet接口的唯一实现类,TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序 和定制排序,其中自然排序为默认的排序方式。向TreeSet中加入的应该是同一个类的对象。
TreeSet判断两个对象不相等的方式是两个对象通过equals方法返回false,或者通过CompareTo方法比较没有返回0
自然排序
自然排序使用要排序元素的CompareTo(Object obj)方法来比较元素之间大小关系,然后将元素按照升序排列。
Java提供了一个Comparable接口,该接口里定义了一个compareTo(Object obj)方法,该方法返回一个整数值,实现了该接口的对象就可以比较大小。
obj1.compareTo(obj2)方法如果返回0,则说明被比较的两个对象相等,如果返回一个正数,则表明obj1大于obj2,如果是 负数,则表明obj1小于obj2。
如果我们将两个对象的equals方法总是返回true,则这两个对象的compareTo方法返回应该返回0
定制排序
自然排序是根据集合元素的大小,以升序排列,如果要定制排序,应该使用Comparator接口,实现 int compare(T o1,T o2)方法。

最重要:

1、TreeSet 是二差树实现的,Treeset中的数据是自动排好序的,不允许放入null值。 

2、HashSet 是哈希表实现的,HashSet中的数据是无序的,可以放入null,但只能放入一个null,两者中的值都不能重复,就如数据库中唯一约束。 

3、HashSet要求放入的对象必须实现HashCode()方法,放入的对象,是以hashcode码作为标识的,而具有相同内容的 String对象,hashcode是一样,所以放入的内容不能重复。但是同一个类的对象可以放入不同的实例 。

HashSet与HashMap的区别:

HashMap HashSet
实现了Map接口 实现Set接口
存储键值对 仅存储对象
调用put()向map中添加元素 调用add()方法向Set中添加元素
HashMap使用键(Key)计算Hashcode

HashSet使用成员对象来计算hashcode值,

对于两个对象来说hashcode可能相同,

所以equals()方法用来判断对象的相等性,

如果两个对象不同的话,那么返回false

HashMap相对于HashSet较快,因为它是使用唯一的键获取对象 HashSet较HashMap来说比较慢
 

JAVA集合框架

简述泛型、反射、注解应用场景及各自解决了哪些问题

泛型:

  类型泛化,即由具体的、个别的扩大为一般的。就是通过类型参数化,来解决程序的通用性设计和实现的若干问题。比如:

  • 编译器类型检查问题
  • 强制类型转换问题
  • 可读性,灵活性问题

泛型通配符约定:

<? extends T> 上界通配符

上界通配符顾名思义,<? extends T>表示的是类型的上界(包含自身),因此通配的参数化类型可能是T或T的子类。正因为无法确定具体的类型是什么,add方法受限(可以添加null,因为null表示任何类型),但可以从列表中获取元素后赋值给父类型。如上图中的第一个例子,第三个add()操作会受限,原因在于List<Animal>和List<Cat>是List<? extends Animal>的子类型。

<? super T> 下界通配符

下界通配符<? super T>表示的是参数化类型是T的超类型(包含自身),层层至上,直至Object,编译器无从判断get()返回的对象的类型是什么,因此get()方法受限。但是可以进行add()方法,add()方法可以添加T类型和T类型的子类型,如第二个例子中首先添加了一个Cat类型对象,然后添加了两个Cat子类类型的对象,这种方法是可行的,但是如果添加一个Animal类型的对象,显然将继承的关系弄反了,是不可行的。

<?> 无界通配符

在理解了上界通配符和下界通配符之后,其实也自然而然的理解了无界通配符。无界通配符用<?>表示,?代表了任何的一种类型,能代表任何一种类型的只有null(Object本身也算是一种类型,但却不能代表任何一种类型,所以List<Object>和List<null>的含义是不同的,前者类型是Object,也就是继承树的最上层,而后者的类型完全是未知的)。

 

反射

定义:

用一句话来解释反射的定义:自控制,自描述。即通过反射可以动态的获取类、属性、方法的信息,也能构造对象并控制对象的属性和行为。

应用:

JDBC的驱动加载方式是通过反射机制实现的,JDBC只是设计了驱动需要实现的接口,并不关心驱动厂商的个数和实现方式,只要安装统一的规范即可,至于类型的判断和具体方法的触发,交给运行期动态判断即可,这种反射机制的使用淋漓尽致的体现了多态,并且降低了类与类之间的耦合度。

注解

定义:可以把注解理解为代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。

应用:@Override   @controller  

Thread类的方法有哪些,如何多种方式实现线程同步

多线程有两种实现方法,一是继承Thread类,重写方法run(),二是实现Runnable接口,实现方法run();

同步有两种实现方法,分别是synchronized、wait与notify。

进程与线程的区别,JAVA中有哪些方式可以创建线程。

参考:java中进程与线程--三种实现方式

进程:系统分配资源的基本单位,资源独立

线程:系统(CPU)调度的基本单位,也是程序执行的最小单位。与同进程中的其他线程共享数据,但拥有自己的栈空间,拥有独立的执行序列

关系:一个线程可以创建和撤销另一个线程;同一个进程中的多个线程之间可以并发执行.简而言之,一个程序至少有一个进程,一个进程至少有一个线程.

创建方式:

  • 继承自Thread类并重写 run() 方法,
  • 实现 Runnable 接口并重写 run() 方法
  • 使用callable和future创建线程

进程的五种基本状态

è¿éåå¾çæè¿°

  • 创建状态:进程在创建时需要申请一个空白PCB,向其中填写控制和管理进程的信息,完成资源分配。如果创建工作无法完成,比如资源无法满足,就无法被调度运行,把此时进程所处状态称为创建状态
  • 就绪状态:进程已经准备好,已分配到所需资源,只要分配到CPU就能够立即运行
  • 执行状态:进程处于就绪状态被调度后,进程进入执行状态
  • 阻塞状态:正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用
  • 终止状态:进程结束,或出现错误,或被系统终止,进入终止状态。无法再执行

现有一学生表结构(student_id,class_id,name),请写出统计每班有多少学生的SQL语句

select class_id,count(*) from student group by class_id

假如你正在开发一个系统的登录程序,请简述你是如何实现记住用户名和密码这个操作的,并如何实现?

login.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
      pageEncoding="UTF-8"%>
  <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
  <html>
  <head>
  <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  <title>Insert title here</title>
  
  <%
     String username = "";
     String password = "";
     //获取当前站点的所有Cookie
     Cookie[] cookies = request.getCookies();
     for (int i = 0; i < cookies.length; i++) {//对cookies中的数据进行遍历,找到用户名、密码的数据
         if ("username".equals(cookies[i].getName())) {
             username = cookies[i].getValue();
         } else if ("password".equals(cookies[i].getName())) {
             password = cookies[i].getValue();
         }
     }
 %>
 
 </head>
 <body>
     <form action="login_handler.jsp" method="post">
         username:<input type="text" name="name" value="<%=username%>" /><br/>
         password:<input type="password" name="pwd" value="<%=password%>" /><br/>
         <input type="checkbox" value="y" name="isLogin">自动登录<br/> 
         <input type="submit" value="登录" />
     </form>
 </body>
 </html>

controller:

@RequestMapping(value = "login", method = RequestMethod.POST)
	public String login(String firstname, String lastname,ModelMap model,HttpServletRequest request,HttpServletResponse response) {
		User user=userService.findByUsername(firstname);
		if(user!=null && lastname!=null){
			if(lastname.equals(user.getPassword())){
				model.put("username", firstname);
			    HttpSession session = request.getSession(); 
				session.setAttribute("users",user);
				model.put("nowtime", new Date());
				model.put("users", user);
				String flag=request.getParameter("remember");
				if("1".equals(flag)){
					//创建两个cookie对象
					Cookie namecookie=new Cookie("username",firstname);
					//设置cookie的有效天为一周
					namecookie.setMaxAge(60*60*24*7);
					Cookie passcookie=new Cookie("password",lastname);
					passcookie.setMaxAge(60*60*24*7);
					response.addCookie(namecookie);
					response.addCookie(passcookie);
				}
				return "redirect:/ toList.do";
			}
		}else{
			model.put("error", "账号或密码错误");
			return "manager/login";
		}
		return "manager/login";
	}

在视图层不支持存储cookie,服务端不支持session的场景下如何保持用户登陆状态。

视图层生成并发送sessionID(视图层的每一次浏览器请求都携带此sessionID),服务端接收sessionID并据此查询用户登录信息

黑盒测试、灰盒测试、白盒测试、单元测试有什么区别?

黑盒测试关注程序的功能是否正确,面向实际用户;

白盒测试关注程序源代码的内部逻辑结构是否正确,面向编程人员;

灰盒测试是介于白盒测试与黑盒测试之间的一种测试。

单元测试(Unit Testing)是对软件基本组成单元进行的测试,如函数或是一个类的方法。这里的单元,就是软件设计的最小单位。

怎么对数据库百万级数据进行优化?

使用读写分离技术(

主数据库(master)处理事务性增、改、删操作(INSERT、UPDATE、DELETE),
从数据库(slave)处理SELECT查询操作

springmvc生命周期

  1. 客户端发出http请求,只要请求形式符合web.xml文件中配置的*.action的话,就由DispatcherServlet来处理。
  2. DispatcherServlet再将http请求委托给映射器的对象来将http请求交给对应的Action来处理
  3. 映射器根据客户的http请求,再对比<bean name="/hello.action如果匹配正确,再将http请求交给程序员写的Action
  4. 执行Action中的业务方法,最终返回一个名叫ModelAndView的对象,其中封装了向视图发送的数据和视图的逻辑名
  5. ModelAndView对象随着响应到到DispatcherServlet中了
  6. 这时DispatcherServlet收到了ModelAndView对象,它也不知道视图逻辑名是何意,又得委托一个名叫视图解析器的对象去具体解析ModelAndView对象中的内容
  7. 将视图解析器解析后的内容,再次交由DispatcherServlet核心控制器,这时核心控制器再将请求转发到具体的视图页面,取出数据,再显示给用户

servlet生命周期

  • Servlet 通过调用 init () 方法进行初始化。
  • Servlet 调用 service() 方法来处理客户端的请求。
  • Servlet 通过调用 destroy() 方法终止(结束)。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的

ajax怎么解决跨域?

  • 代理
  • JSONP
  • 添加响应头

       addHeader(‘Access-Control-Allow-Origin:*’);//允许所有来源访问 
  addHeader(‘Access-Control-Allow-Method:POST,GET’);//允许访问的方式

Mysql索引类型

  • 普通索引
  • 唯一索引
  • 主键索引
  • 组合索引
  • 全文索引

参考:Mysql索引

SpringCloud是什么

Spring Cloud 是致力于分布式系统、云服务的框架。

为开发人员提供了快速构建分布式系统中一些常见模式的工具,例如:

  • 配置管理
  • 服务注册与发现
  • 断路器
  • 智能路由
  • 服务间调用
  • 负载均衡
  • 微代理
  • 控制总线
  • 一次性令牌
  • 全局锁
  • 领导选举
  • 分布式会话
  • 集群状态
  • 分布式消息
  • ……

参考:史上最全的 Spring Cloud 教程

Hibernate的三种状态是什么?怎么将游离状态转换为持久化状态?

对Java线程安全与不安全的理解

定义:

存在成员变量的类用于多线程时是不安全的,不安全体现在这个成员变量可能发生非原子性的操作,而变量定义在方法内也就是局部变量是线程安全的。想想在使用struts1时,不推荐创建成员变量,因为action是单例的,如果创建了成员变量,就会存在线程不安全的隐患,而struts2是每一次请求都会创建一个action,就不用考虑线程安全的问题。所以,日常开发中,通常需要考虑成员变量或者说全局变量在多线程环境下,是否会引发一些问题

经典案例:

多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款,此为有序性。

对原子性的理解:

我们可以把左边的圆看成是一行代码,右边的圆被分割成了两行代码。如果数据没有破坏原子性,由于线程被调度一次的最少要执行1行代码,那么t1只要执行了这行代码,就会连读带写全部完成,其他线程再拿到的数据就是被写过的最新数据,不会有任何安全隐患;而如果数据破坏了原子性,将读写进行了分割,那么t1,读取完数据如果停掉的话,t2执行的时候拿到的就是一个老数据(没有被更新的数据),接下来t1,t2同时对相同的老数据进行更新势必会因此数据的异常。

注意:

  1. 只存在读数据的时候,不会产生线程安全问题。
  2. 在java中,只有同时操作成员(全局)变量的时候才会产生线程安全问题,局部变量不会(每个线程执行时将会把局部变量放在各自栈帧的工作内存中,线程间不共享,故不存在线程安全问题,

解决方法:

  1. 使用synchronized解决线程安全问题
  • 同步代码块
  • 同步函数
  • 静态同步函数

可参考:线程安全问题

关于锁

  • 乐观锁:

顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制。乐观锁适用于多读的应用类型,这样可以提高吞吐量,像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。

  • 悲观锁

顾名思义,就是很悲观,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会block直到它拿到锁。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。

  • 两种锁各有优缺点,不可认为一种好于另一种,像乐观锁适用于写比较少的情况下,即冲突真的很少发生的时候,这样可以省去了锁的开销,加大了系统的整个吞吐量。但如果经常产生冲突,上层应用会不断的进行retry,这样反倒是降低了性能,所以这种情况下用悲观锁就比较合适(比如Synchronized就是悲观锁)。

死锁及其解决方案(避免、预防、检测)

所谓死锁:是指两个或两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁

死锁产生的原因?

1.因竞争资源发生死锁 现象:系统中供多个进程共享的资源的数目不足以满足全部进程的需要时,就会引起对诸资源的竞争而发生死锁现象

2.进程推进顺序不当发生死锁

死锁的四个必要条件:

(1)互斥条件:进程对所分配到的资源不允许其他进程进行访问,若其他进程访问该资源,只能等待,直至占有该资源的进程使用完成后释放该资源

(2)请求和保持条件:进程获得一定的资源之后,又对其他资源发出请求,但是该资源可能被其他进程占有,此事请求阻塞,但又对自己获得的资源保持不放

(3)不可剥夺条件:是指进程已获得的资源,在未完成使用之前,不可被剥夺,只能在使用完后自己释放

(4)环路等待条件:是指进程发生死锁后,必然存在一个进程--资源之间的环形链

处理死锁的基本方法

预防死锁(破坏四个必要条件)

  • 资源一次性分配:(破坏请求和保持条件)
  • 可剥夺资源:即当某进程新的资源未满足时,释放已占有的资源(破坏不可剥夺条件)
  • 资源有序分配法:系统给每类资源赋予一个编号,每一个进程按编号递增的顺序请求资源,释放则相反(破坏环路等待条件)

避免死锁(银行家算法):

预防死锁的几种策略,会严重地损害系统性能。因此在避免死锁时,要施加较弱的限制,从而获得 较满意的系统性能。由于在避免死锁的策略中,允许进程动态地申请资源。因而,系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全状态,则将资源分配给进程;否则,进程等待。其中最具有代表性的避免死锁算法是银行家算法

检测死锁

  • 首先为每个进程和每个资源指定一个唯一的号码;
  • 然后建立资源分配表和进程等待表,
  • 通过系统所设置的检测机构,及时地检测出死锁的发生,并精确地确定与死锁有关的进程和资源,然后采取适当措施,从系统中将已发生的死锁清除掉。

解除死锁:

当发现有进程死锁后,便应立即把它从死锁状态中解脱出来,常采用的方法有:

  • 剥夺资源:从其它进程剥夺足够数量的资源给死锁进程,以解除死锁状态;
  • 撤消进程:可以直接撤消死锁进程或撤消代价最小的进程,直至有足够的资源可用,死锁状态.消除为止;所谓代价是指优先级、运行代价、进程的重要性和价值等。

另外可补充:这是我见过最有用的java面试题,面试了无数公司总结的

猜你喜欢

转载自blog.csdn.net/qq_28202661/article/details/81782963
今日推荐