基础部分
面向对象
1.谈谈你对多态的理解。
答:在java中对于多态的理解是很重要的
多态的定义: 允许不同类的对象对同一消息做出相应,同一消息可以根据发送对象的不同而采用多种不同的行为方式,(发送消息指的是函数调用) 实现多态的技术为动态绑定,是指在执行期间判断所引用对象的实际类型,根据其实际类型调用相应的方法
多态的作用: 消除类型之间的耦合关系
多态存在的三个必要条件: 1.要有继承 2.要有重写 3.父类指向子类对象
多态的好处 1.可替换性:多态对已存在代码具有可替换性 例如 多态对圆类工作 对其他几何圆类几个图形一样工作(圆环)
2.可扩充性:多态对代码具有可扩充性,增加新的子类不会影响已存在类的多态 继承 以及其他特性的运行和操作,并且增加子类更容易获得多态功能,在实现了圆锥 半圆锥的半球体的添加的基础上,更容易对其他的球体的添加
3.接口性:多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。
4.灵活性:它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性:多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要。java中多态的实现方法 接口实现 继承父类进行方法重写 同一个类中进行方法重载
2.你是如何使用public,private,protected,default这些访问修饰符的?
答:访问权限不同,使用时尽量使用小范围的
3.final修饰引用类型和原始类型时有什么不同?
答:当使用final修饰基本类型变量时,不能对基本类型变量重新赋值,因此基本类型变量不能被改变。但对于引用类型的变量而言,它保存的仅仅是一个引用,final只保证这个引用所引用的地址不会改变,即一直引用同一个对象,但这个对象完全可以发生改变
4.java中是值传递还是引用传递?
答:值传递
5.请你例举出两个使用重载的原因。
答: (1)同一个类中用相同的名称定义两个或者多个方法,但是参数项不同同一个类中用相同的名称定义两个或者多个方法,但是参数项不
(2)同根据参数类型调用相应参数的处理方法
6.你在编程时,何时使用组合,何时使用继承?
答:当我们不需要创建新的组件来完成代码复用,而只需要通过对象组合的方法来拼装已存在的组件以获取新的功能,我使用组合。如果现有的组件总是显得不够,而其他类中有需要的组件,这时使用继承
7.请你分析一下JAVA的加载过程。
答:类从被加载到JVM中开始,到卸载为止,整个生命周期包括:加载、验证、准备、解析、初始化、使用和卸载七个阶段。
8.接口可以包含字段吗?如果可以,它们默认的修饰符是什么?
答:可以,public,static,final(一个接口可以有三种类型的成员:常量字段,抽象,静态和默认方法,静态类型作为嵌套接口和类,一个接口不能有可变的实例和类变量)
9.static的方法和一般的方法有什么区别?
答:1、在外部调用静态方法时,可以使用"类名.方法名"的方式,也可以使用"对象名.方法名"的方式。而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象。
2、静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。
3、程序的static块只能调用静态方法,不能调用实例的方法。
10.低聚合低耦合,还是高聚合低耦合?
答:高聚合低耦合
B.异常处理
1.Error和Exception继承自哪个类?
答:都继承Throwable类
2.在子类中覆盖父类的方法,如何处理父类方法声明的异常。
答:(1)提高父类异常捕获范围(2)try{}catch{}
3.例举一下你经常使用的异常类。
答: 方法的参数错误:illegalargumentexception
没有访问权限:illegalaccessexception
空指针异常类:NullPointerException
类型强制转换异常:ClassCastException
数组下标越界异常:ArrayIndexOutOfBoundsException
文件未找到异常:FileNotFoundException
字符串转换为数字异常:NumberFormatException
操作数据库异常:SQLException
输入输出异常:IOException
C.类型问题
1.RTTI是什么?
答:RTTI 是“Runtime Type Information”的缩写,意思是:运行时类型信息。它提供了运行时确定对象类型的方法
2.Class.forName(“类名”)与直接调用构造方法新建对象有什么不同?
答:Class.forName使用了类的动态加载机制,使类的调用和类的实现解耦,类的调用不再依赖于类的实现,实现了oo设计原则中的依赖注入原则,缺点是只能调用默认无参构造器。
new关键字直接创建一个新对象,这要操作的执行效率更高,并且能够调用所有的public构造器。
3.boolean.class与Boolean.type有什么不同?
答:1、boolean是基础数据类型,Boolean是其包装类,也就是属于类;
2、boolean一般存于栈空间中,Boolean对象存于堆空间中;
3、boolean有true和false两种值,Boolean除了true和false以外,还有null;
4、Java5.0以前,不能把Boolean用于条件语句,需要转换以boolean以后再使用。
4.java在何时获得对象的确切类型?
答:java在运行时获得对象的确切类型
D.数据结构
1.数组和容器的区别?
答:1.数组可以存储基本数据类型,也可以存储引用数据类型,集合只能存储引用数据类型。
2.数组是固定长度的,集合的长度是可变的。
3.数组存储的元素必须是同一个数据类型;集合存储的对象可以是不同数据类型。
操作数组的工具类: Arrays(数组排序、数组转化成集合等功能);
操作集合的工具类:Collections(集合排序、集合转化成数组等功能);
2.方法的返回值可以是数组吗?
答:可以
3.java一共有几种容器,例举出来。
答:List、ArrayList、Vector及map、HashTable、HashMap
4.ArrayList和LinkedList的底层实现有什么不同?
答:ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构
5.常用的几种排序算法的时间复杂度?
答:
6.用java代码实现一个双向链表。
1 package structure;
2
3 import java.util.Arrays;
4 import java.util.Scanner;
5 import static net.mindview.util.Print.*;
6
7 /**
8 * 双向链表的操作
9 * @author Tom Tao
10 *
11 */
12 class Node {
13 public int value;
14 public Node(int n) {
15 this.value = n;
16 }
17 Node pre;
18 Node next;
19 }
20
21 public class ReLinkedList {
22 public static Node head;
23 public static int nodeNum;
24
25 /*初始化*/
26 public static void init(int n[]) {
27 head = new Node(n[0]);
28 Node temp = null;
29 head.pre = null;
30 Node p = head;
31 nodeNum ++;
32 for(int i = 1 ; n[i] != -1 ; i ++) {
33 nodeNum ++;
34 temp = new Node(n[i]);
35 p.next = temp;
36 temp.pre = p;
37 p = temp;
38 }
39 p.next = null;
40 }
41
42 /*打印*/
43 public static void printList(Node h) {
44 while(h != null) {
45 printnb(h.value + " ");
46 h = h.next;
47 }
48 }
49
50 /*插入*/
51 public static void insert(int index, int v) {
52 Node ne = new Node(v);
53 if(index == 0) {
54 ne.next = head;
55 ne.pre = null;
56 head = ne;
57 return;
58 }
59 Node temp = head;
60 Node t = temp;
61 for(int i = 0 ; i < index ; i ++) {
62 t = temp;
63 temp = temp.next;
64 }
65 if(index == nodeNum) {
66 t.next = ne;
67 ne.pre = t;
68 ne.next = null;
69 return;
70 }
71 ne.next = temp;
72 ne.pre = t;
73 temp.pre = ne;
74 t.next = ne;
75 }
76
77 /*删除*/
78 public static void delete(int index) {
79 if(index == 0){
80 head = head.next;
81 head.pre = null;
82 return;
83 }
84 Node temp = head;
85 Node t = temp;
86 for(int i = 0 ; i < index ; i ++) {
87 t = temp;
88 temp = temp.next;
89 }
90 if(index == nodeNum) {
91 t.next = null;
92 return;
93 }
94 t.next = temp.next;
95 temp.next.pre = t;
96 }
97
98 /*查找*/
99 public static int find(int index) {
100 Node temp = head;
101 for(int i = 0 ; i < index ; i ++) {
102 temp = temp.next;
103 }
104 return temp.value;
105 }
106
107 /*test*/
108 public static void main(String[] args) {
109 Scanner sc = new Scanner(System.in);
110 print("please input the nums to init:");
111 int[] n = new int[100];
112 Arrays.fill(n, -1);
113 int val;
114 int count = 0;
115 while((val = sc.nextInt()) >= 0) {
116 n[count++] = val;
117 }
118 init(n);
119 while(true){
120 printList(head);
121 print("\nplease input the index and num to insert");
122 int index = sc.nextInt();
123 val = sc.nextInt();
124 insert(index, val);
125 printList(head);
126 print("\nplease input the index to delete");
127 index = sc.nextInt();
128 delete(index);
129 printList(head);
130 print("\nplease input the index to find");
131 index = sc.nextInt();
132 print(find(index));
133 print("----------------------------------------------------");
134 }
135 }
136 }
E.多线程:
1.JAVA中是单进程多线程还是多进程多线程?
答:多进程多线程
2.列举出一种出现死锁的情况?java是否解决了死锁问题?
答:一个线程T1持有锁L1并且申请获得锁L2,而另一个线程T2持有锁L2并且申请获得锁L1,因为默认的锁申请操作都是阻塞的,所以线程T1和T2永远被阻塞了。导致了死锁。这是最容易理解也是最简单的死锁的形式
解决了
3.如何解决死锁问题?
答:1.如果想要打破互斥条件,我们需要允许进程同时访问某些资源,这种方法受制于实际场景,不太容易实现条件;
2.打破不可抢占条件,这样需要允许进程强行从占有者那里夺取某些资源,或者简单一点理解,占有资源的进程不能再申请占有其他资源,必须释放手上的资源之后才能发起申请,这个其实也很难找到适用场景;
3.进程在运行前申请得到所有的资源,否则该进程不能进入准备执行状态。这个方法看似有点用处,但是它的缺点是可能导致资源利用率和进程并发性降低;
4.避免出现资源申请环路,即对资源事先分类编号,按号分配。这种方式可以有效提高资源的利用率和系统吞吐量,但是增加了系统开销,增大了进程对资源的占用时间。
4.一个对象被锁定之后,其他的方法是否可以访问这个对象?
答:1. 获得当前对象锁的线程,再调用加锁的方法,不会阻塞
调用该对象普通方法(即没有同步的方法),不会阻塞
既没有获得锁,调用的又是同步方法或代码块,会被阻塞
F.JSP/SERVLET:**
1.jsp的内置对象及其作用
答:request:用户端请求,此请求会包含来自GET/Post请求的参数;
response:网页传回用户端的回应。
pageContext:页面的属性是在这里管理
session:与请求有关的会话期
application :Servlet正在执行的内容
out :用来传递回应的输出
config :servlet的构架部件
page :jsp网页本身
exception :针对错误的网页。未捕捉的例外
2.servlet的生命周期
答:1.只有一个Servlet对象(要点)
2.第一次请求的时候被初始化,只此一遍
3.初始化后先调用init方法,只此一遍
4.每个请求,调用一遍service -> service -> doGet/doPost。以多线程的方式运行
5.卸载前调用destroy方法
3.转发和重定向的区别
答:转发在服务器端完成的;重定向是在客户端完成的
转发的速度快;重定向速度慢
转发的是同一次请求;重定向是两次不同请求
转发不会执行转发后的代码;重定向会执行重定向之后的代码
转发地址栏没有变化;重定向地址栏有变化 的服务器下完成
4.Filter和Servlet的区别。
答:区别
5.何时使用转发何时使用重定向
答:前后两个页面 有数据传递 用请求转发,没有则用重定向。
比如servlet查询了数据需要在页面显示,就用请求转发。
比如servlet做了update操作跳转到其他页面,就用重定向。
G.JDBC:
1.实现一个数据库的链接。
答:
2.简述链接数据库的步骤。 答: 创建一个以JDBC连接数据库的程序,包含7个步骤:
1、加载JDBC驱动程序:
2、提供JDBC连接的URL
3、创建数据库的连接
4、创建一个Statement
5、执行SQL语句
6、处理结果,两种情况:
执行更新返回的是本次操作影响到的记录数。
执行查询返回的结果是一个ResultSet对象。
7、关闭JDBC对象
H.数据库:
1.连接查询,视图 答:
连接数据库工具类:SqlConnection;
package com.jsp.util;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.Statement;
/**
* 连接数据库工具类
* @author Administrator
*/
public class SqlConnection {
/**
* 获取数据库连接
*
* @return java.sql.Connection
*/
public static final Connection getSqlConn() {
try {
// 1.加载mysql数据库驱动
/* 动态加载一个class文件: Class.forName("com.jdbc01.test.Users"); */
Class.forName("com.mysql.jdbc.Driver");
// 2.设置mysql数据库用户账户(用户名和密码)
String user = "root";
String pwd = "123456";
// 3.设置数据库地址或URL
String url = "jdbc:mysql://127.0.0.1:3306/j2ee?useUnicode=true&characterEncoding=utf-8";
// 4.获取数据库连接对象
Connection conn = DriverManager.getConnection(url, user, pwd);
// System.out.println(conn);
return conn;
} catch (Exception e) {
// e.printStackTrace();
System.out.println("数据库连接失败!");
}
return null;
}
/**
* 关闭资源
*
* @param conn
* @param state
* @param rs
*/
public static final void closeConn(Connection conn, Statement state, ResultSet rs) {
// 关闭资源,ResultSet->Statement->Connection
try {
if (rs != null) {
rs.close();
}
if (state != null) {
state.close();
}
conn.close();
} catch (Exception e) {
System.out.println("关闭资源异常");
e.printStackTrace();
}
}
}
实体类:Employee;
package com.jsp.model;
/**
* 员工类, 用于存储和封装员工数据
*
* @author Administrator
*
*/
public class Employee {
private String emp_no; // 员工编号
private String emp_name; // 员工姓名
private String mobile; // 员工手机号
private int sex; // 性别
private String birthday; // 出生日期
private int salary; // 薪水
private String dep_name; // 部门名称
private String homeplace; // 籍贯
public Employee() {
}
public Employee(String emp_no, String emp_name, String mobile, int sex, String birthday, int salary,
String dep_name, String homeplace) {
super();
this.emp_no = emp_no;
this.emp_name = emp_name;
this.mobile = mobile;
this.sex = sex;
this.birthday = birthday;
this.salary = salary;
this.dep_name = dep_name;
this.homeplace = homeplace;
}
public String getEmp_no() {
return emp_no;
}
public void setEmp_no(String emp_no) {
this.emp_no = emp_no;
}
public String getEmp_name() {
return emp_name;
}
public void setEmp_name(String emp_name) {
this.emp_name = emp_name;
}
public String getMobile() {
return mobile;
}
public void setMobile(String mobile) {
this.mobile = mobile;
}
public int getSex() {
return sex;
}
public void setSex(int sex) {
this.sex = sex;
}
public String getBirthday() {
return birthday;
}
public void setBirthday(String birthday) {
this.birthday = birthday;
}
public int getSalary() {
return salary;
}
public void setSalary(int salary) {
this.salary = salary;
}
public String getDep_name() {
return dep_name;
}
public void setDep_name(String dep_name) {
this.dep_name = dep_name;
}
public String getHomeplace() {
return homeplace;
}
public void setHomeplace(String homeplace) {
this.homeplace = homeplace;
}
@Override
public String toString() {
return "Employee [emp_no=" + emp_no + ", emp_name=" + emp_name + ", mobile=" + mobile + ", sex=" + sex
+ ", birthday=" + birthday + ", salary=" + salary + ", dep_name=" + dep_name + ", homeplace="
+ homeplace + "]";
}
}
数据库操作类: EmployeeDao;
package com.jsp.dao;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.jsp.model.Employee;
import com.jsp.util.SqlConnection;
/**
* 实现员工表增删改查
* @author Administrator
*/
public class EmployeeDao {
/**
* 登录
*
* @param mobile
* @param pwd
* @return
*/
public boolean login(String mobile, String pwd) {
Connection conn = SqlConnection.getSqlConn();
if (conn != null) {
// 执行SQL语句Statement
PreparedStatement ps = null;
// 查询到的结果集初始化null
ResultSet rs = null;
String sql = "select emp_no from employee where mobile = ? and pwd = ?";
try {
ps = conn.prepareStatement(sql);
ps.setString(1, mobile);
ps.setString(2, pwd);
rs = ps.executeQuery();
if (rs.next()) {
return true;
}
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
SqlConnection.closeConn(conn, ps, rs);
}
return false;
}
/**
* 查询所有
* @return
*/
public List queryEmp() {
List list = new ArrayList();
Employee emp = new Employee();
Connection conn = SqlConnection.getSqlConn();
if (conn != null) {
// 执行SQL语句Statement
Statement state = null;
// 查询到的结果集初始化null
ResultSet rs = null;
String sql = "select emp_no as no, emp_name, mobile, sex, birthday, salary, dep_name, homeplace from employee";
try {
state = conn.createStatement();
rs = state.executeQuery(sql);
while (rs.next()) {
// 有别名的字段,根据别名获取内容
String emp_no = rs.getString("no");
// 根据字段序列号获取内容
String emp_name = rs.getString(2);
String mobile = rs.getString("mobile");
int sex = rs.getInt("sex");
String birthday = rs.getString("birthday");
int salary = rs.getInt("salary");
String dep_name = rs.getString("dep_name");
String homeplace = rs.getString("homeplace");
emp = new Employee(emp_no, emp_name, mobile, sex, birthday, salary, dep_name, homeplace);
list.add(emp);
}
} catch (SQLException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
SqlConnection.closeConn(conn, state, rs);
}
return list;
}
/**
* 添加员工(注册)
*
* @param emp_no
* @param emp_name
* @param mobile
* @param pwd
* @param sex
* @param birthday
* @param salary
* @param dep_name
* @param homeplace
*/
public boolean addEmployee(String emp_no, String emp_name, String mobile,String pwd, int sex, String birthday, int salary,
String dep_name, String homeplace) {
// 1.获取数据库连接对象
Connection conn = SqlConnection.getSqlConn();
// 3.创建PreparedStatement对象
PreparedStatement ps = null;
// 2.定义SQL语句
String sql = "insert into employee(emp_no,emp_name,mobile,pwd,sex,birthday,salary,dep_name,homeplace) values(?,?,?,?,?,?,?,?,?)";
try {
// 并预编译SQL语句
ps = conn.prepareStatement(sql);
// 4.给占位符赋值
ps.setString(1, emp_no);
ps.setString(2, emp_name);
ps.setString(3, mobile);
ps.setString(4, pwd);
ps.setInt(5, sex);
ps.setString(6, birthday);
ps.setInt(7, salary);
ps.setString(8, dep_name);
ps.setString(9, homeplace);
// 5.执行SQL语句
int rows = ps.executeUpdate();
System.out.println("insert操作影响行数:" + rows);
} catch (SQLException e) {
e.printStackTrace();
return false;
}
SqlConnection.closeConn(conn, ps, null);
return true;
}
/**
* 根据员工编号删除员工信息
*
* @param emp_no
*/
public void delEmployee(String emp_no) {
// 1.获取数据库连接对象
Connection conn = SqlConnection.getSqlConn();
// 2.定义SQL语句
String sql = "delete from employee where emp_no = ?;";
// 3.通过Connection创建PreparedStatement对象
PreparedStatement ps = null;
try {
// 并预编译SQL语句
ps = conn.prepareStatement(sql);
// 4.给占位符赋值
ps.setString(1, emp_no);
// 5.执行SQL语句
int rows = ps.executeUpdate();
System.out.println("delete操作影响行数:" + rows);
} catch (SQLException e) {
e.printStackTrace();
}
SqlConnection.closeConn(conn, ps, null);
}
/**
* 根据员工编号修改员工的手机号码、薪水、部门名称
*
* @param emp_no
* @param mobile
* @param salary
* @param dep_name
*/
public void updateEmployee(String emp_no, String mobile, int salary, String dep_name) {
// 1.获取数据库连接
Connection conn = SqlConnection.getSqlConn();
// 2.定义SQL语句
String sql = "update employee set mobile = ?, salary = ?, dep_name = ? where emp_no = ?;";
// 3.通过Connection创建PreparedStatement对象
PreparedStatement ps = null;
try {
// 并预编译SQL语句
ps = conn.prepareStatement(sql);
// 4.赋值
ps.setString(1, mobile);
ps.setInt(2, salary);
ps.setString(3, dep_name);
ps.setString(4, emp_no);
// 5.执行SQL语句
int rows = ps.executeUpdate();
System.out.println("update操作影响行数:" + rows);
} catch (SQLException e) {
e.printStackTrace();
}
SqlConnection.closeConn(conn, ps, null);
}
/**
* 根据员工编号获取员工信息
*
* @param emp_no
* @return
*/
public Employee getEmployee(String emp_no) {
Employee emp = new Employee();
// 1.开启资源,连接数据库
Connection conn = SqlConnection.getSqlConn();
// 2.定义SQL语句
String sql = "select emp_no, emp_name, mobile, sex, birthday, salary, dep_name, homeplace from employee where emp_no = ?";
// 3.定义PreparedStatement和ResultSet
PreparedStatement ps = null;
ResultSet rs = null;
try {
// 4.准备SQL语句
ps = conn.prepareStatement(sql);
// 5.给占位符赋值
ps.setString(1, emp_no);
// 6.执行
rs = ps.executeQuery();
// 7.处理结果集
while (rs.next()) {
String emp_name = rs.getString(2);
String mobile = rs.getString("mobile");
int sex = rs.getInt("sex");
String birthday = rs.getString("birthday");
int salary = rs.getInt("salary");
String dep_name = rs.getString("dep_name");
String homeplace = rs.getString("homeplace");
emp = new Employee(emp_no, emp_name, mobile, sex, birthday, salary, dep_name, homeplace);
}
} catch (SQLException e) {
e.printStackTrace();
}
// 8.关闭资源
SqlConnection.closeConn(conn, ps, rs);
return emp;
}
}
测试类: test01;
package com.jsp.test;
import java.util.List;
import com.jsp.dao.EmployeeDao;
import com.jsp.model.Employee;
public class Test01 {
public static void main(String[] args) {
EmployeeDao empdao = new EmployeeDao();
List list = empdao.queryEmp();
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i).toString());
}
}
}
2.数据表的设计(一对一,一对多,多对多) 答:
1、一对一可以两个实体设计在一个数据库中
2、一对多可以建两张表,将一这一方的主键作为多那一方的外键
3、多对多可以多加一张中间表,将另外两个表的主键放到这个表中 3.为什么会发生事务死锁,解决方法? 答:为什么会发生事务死锁?
根据操作系统中的定义:死锁是指在一组进程中的各个进程均占有不会释放的资源,但因互相申请被其他进程所站用不会释放的资源而处于的一种永久等待状态。
产生死锁的四个必要条件:
(1)互斥条件:一个资源每次只能被一个进程使用。
(2)请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放。
(3)不剥夺条件:进程已获得的资源,在末使用完之前,不能强行剥夺。
(4)循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。
解决方法?
1.避免死锁
上面列出了死锁的四个必要条件,我们只要想办法破其中的任意一个或多个条件,就可以避免死锁发生,一般有以下几种方法:
(1).按同一顺序访问对象。(注:避免出现循环)
(2).避免事务中的用户交互。(注:减少持有资源的时间,较少锁竞争)
(3).保持事务简短并处于一个批处理中。(注:同(2),减少持有资源的时间)
(4).使用较低的隔离级别。(注:使用较低的隔离级别(例如已提交读)比使用较高的隔离级别(例如可序列化)持有共享锁的时间更短,减少锁竞争)
(5).使用基于行版本控制的隔离级别:2005中支持快照事务隔离和指定READ_COMMITTED隔离级别的事务使用行版本控制,可以将读与写操作之间发生的死锁几率降至最低:
2.处理方法:
(1). 根据sql,查看那个spid处于wait状态,然后用kill spid来干掉(即破坏死锁的第四个必要条件:循环等待);当然这只是一种临时解决方案,我们总不能在遇到死锁就在用户的生产环境上排查死锁、Kill sp,我们应该考虑如何去避免死锁。
(2). 使用SET LOCK_TIMEOUT timeout_period(单位为毫秒)来设定锁请求超时。默认情况下,数据库没有超时期限(timeout_period值为-1,可以用SELECT@LOCK_TIMEOUT来查看该值,即无限期等待)。当请求锁超过timeout_period时,将返回错误。timeout_period值为0时表示根本不等待,一遇到锁就返回消息。设置锁请求超时,破环了死锁的第二个必要条件(请求与保持条件)。
(3). SQL Server内部有一个锁监视器线程执行死锁检查,锁监视器对特定线程启动死锁搜索时,会标识线程正在等待的资源;然后查找特定资源的所有者,并递归地继续执行对那些线程的死锁搜索,直到找到一个构成死锁条件的循环。检测到死锁后,数据库引擎 选择运行回滚开销最小的事务的会话作为死锁牺牲品,返回1205 错误,回滚死锁牺牲品的事务并释放该事务持有的所有锁,使其他线程的事务可以请求资源并继续运行。