目录
1.1单例模式,说下安全问题
懒汉式
不安全:
//懒汉模式(线程不安全)
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
饿汉式 线程安全
public class Singleton {
private static Singleton instance=new Singleton();
private Singleton(){}
public static Singleton getInstance(){
return instance;
}
}
解决懒汉线程安全问题:
安全:
//线程安全
public class Singleton{
private static Singleton instance;
private Singleton(){}
public static synchronized Singleton getInstance(){
if(instance==null){
instance=new Singleton();
}
return instance;
}
}
public class Singleton{
private static volatile Singleton instance;
private Singleton(){}
public static Singleton getInstance(){
// 判断对象是否存在
if(instance == null){//*1号位置**//
// 加锁,保证线程安全
synchronized(Singleton.class){
// 再次判断(第一次获取对象时才是空。对象有了之后,这边不会走)
if(instance == null){
// 创建对象
instance = new Student();
}
}
}
return instance;
}
}
1.2查询数据库比较慢可能是哪些原因造成的
1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷)
2、I/O吞吐量小,形成了瓶颈效应。
3、没有创建计算列导致查询不优化。
4、内存不足
5、网络速度慢
6、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量)
7、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷)
8、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。
9、返回了不必要的行和列
10、查询语句不好,没有优化
1.3如何保证线程安全(进程安全是什么)
线程安全
一般说来,确保线程安全的方法有这几个:
竞争与原子操作、同步与锁、可重入、过度优化。
竞争与原子操作
多个线程同时访问和修改一个数据,可能造成很严重的后果。出现严重后果的原因是很多操作被操作系统编译为汇编代码之后不止一条指令,因此在执行的时候可能执行了一半就被调度系统打断了而去执行别的代码了。一般将单指令的操作称为原子的(Atomic),因为不管怎样,单条指令的执行是不会被打断的。
因此,为了避免出现多线程操作数据的出现异常,Linux系统提供了一些常用操作的原子指令,确保了线程的安全。但是,它们只适用于比较简单的场合,在复杂的情况下就要选用其他的方法了。
同步与锁
为了避免多个线程同时读写一个数据而产生不可预料的后果,开发人员要将各个线程对同一个数据的访问同步,也就是说,在一个线程访问数据未结束的时候,其他线程不得对同一个数据进行访问。
同步的最常用的方法是使用锁(Lock),它是一种非强制机制,每个线程在访问数据或资源之前首先试图获取锁,并在访问结束之后释放锁;在锁已经被占用的时候试图获取锁时,线程会等待,直到锁重新可用。
二元信号量是最简单的一种锁,它只有两种状态:占用与非占用,它适合只能被唯一一个线程独占访问的资源。对于允许多个线程并发访问的资源,要使用多元信号量(简称信号量)。
可重入
一个函数被重入,表示这个函数没有执行完成,但由于外部因素或内部因素,又一次进入该函数执行。一个函数称为可重入的,表明该函数被重入之后不会产生任何不良后果。可重入是并发安全的强力保障,一个可重入的函数可以在多线程环境下放心使用。
过度优化
在很多情况下,即使我们合理地使用了锁,也不一定能够保证线程安全,因此,我们可能对代码进行过度的优化以确保线程安全。
我们可以使用volatile关键字试图阻止过度优化,它可以做两件事:第一,阻止编译器为了提高速度将一个变量缓存到寄存器而不写回;第二,阻止编译器调整操作volatile变量的指令顺序。
在另一种情况下,CPU的乱序执行让多线程安全保障的努力变得很困难,通常的解决办法是调用CPU提供的一条常被称作barrier的指令,它会阻止CPU将该指令之前的指令交换到barrier之后,反之亦然。
1.4实现线程的两种方法
1、继承Thread类
2、实现Runnable接口
1、继承Thread类有一个缺点就是单继承,而实现Runnable接口则弥补了它的缺点,可以实现多继承
2、继承Thread类必须如果产生Runnable实例对象,就必须产生多个Runnable实例对象,然后再用Thread产生多个线程;而实现Runnable接口,只需要建立一个实现这个类的实例,然后用这一个实例对象产生多个线程。即实现了资源的共享性
Runable接口实现的优点:
(1)适合多个相同程序代码的线程去处理同一资源的情况,把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。
(2)可以避免由于Java的单继承特性带来的局限。我们经常碰到这样一种情况,即当我们要将已经继承了某一个类的子类放入多线程中,由于一个类不能同时有两个父类,所以不能用继承Thread类的方式,那么,这个类就只能采用实现Runnable接口的方式了。
(3) 有利于程序的健壮性,代码能够被多个线程共享,代码与数据是独立的。当多个线程的执行代码来自同一个类的实例时,即称它们共享相同的代码。多个线程操作相 同的数据,与它们的代码无关。当共享访问相同的对象是,即它们共享相同的数据。当线程被构造时,需要的代码和数据通过一个对象作为构造函数实参传递进去, 这个对象就是一个实现了Runnable接口的类的实例。
1.5抽象类与接口
抽象类和接口的区别
1. 抽象类中的方法可以有方法体,就是能实现方法的具体功能,但是接口中的方法不行。
2. 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
3. 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
4. 一个类只能继承一个抽象类,而一个类却可以实现多个接口。
接口特性:
接口中每一个方法也是隐式抽象的,接口中的方法会被隐式的指定为 public abstract(只能是 public abstract,其他修饰符都会报错)。
接口中可以含有变量,但是接口中的变量会被隐式的指定为 public static final 变量(并且只能是 public,用 private 修饰会报编译错误)。
接口中的方法是不能在接口中实现的,只能由实现接口的类来实现接口中的方法
1.6事务的四大特性
原子性:事务是最小的执行单位,不允许分割。事务的原子性确保动作要么全部完成,要么完全不起作用;
一致性:执行事务前后,数据保持一致,多个事务对同一个数据读取的结果是相同的;
隔离性:并发访问数据库时,一个用户的事务不被其他事务所干扰,各并发事务之间数据库是独立的;
持久性:一个事务被提交之后。它对数据库中数据的改变是持久的,即使数据库发生故障也不应该对其有任何影响。
1.7数据脏度
脏读(DrityRead):某个事务已更新一份数据,另一个事务在此时读取了同一份数据,由于某些原因,前一个RollBack了操作,则后一个事务所读取的数据就会是不正确的。
不可重复读(Non-repeatable read):在一个事务的两次查询之中数据不一致,这可能是两次查询过程中间插入了一个事务更新的原有的数据。
幻读(PhantomRead):在一个事务的两次查询中数据笔数不一致,例如有一个事务查询了几列(Row)数据,而另一个事务却在此时插入了新的几列数据,先前的事务在接下来的查询中,就会发现有几列数据是它先前所没有的。
1.8事务隔离级别

SQL 标准定义了四个隔离级别:
READ-UNCOMMITTED(读取未提交):最低的隔离级别,允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读。
READ-COMMITTED(读取已提交):允许读取并发事务已经提交的数据,可以阻止脏读,但是幻读或不可重复读仍有可能发生。
REPEATABLE-READ(可重复读):对同一字段的多次读取结果都是一致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
SERIALIZABLE(可串行化):最高的隔离级别,完全服从ACID的隔离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以防止脏读、不可重复读以及幻读。
1.9数据库存储过程与触发器
存储过程是一个预编译的SQL语句,优点是允许模块化的设计,就是说只需要创建一次,以后在该程序中就可以调用多次。如果某次操作需要执行多次SQL,使用存储过程比单纯SQL语句执行要快。
触发器是用户定义在关系表上的一类由事件驱动的特殊的存储过程。触发器是指一段代码,当触发某个事件时,自动执行这些代码。
1.10TCP/IP三次握手过程

所谓协议(protocol),其实就是一个群体之间规定的规则,这个规则的目的是为了保证这个群体里面的人可以正常交流
三次握手:
进行三次握手的主要作用就是为了确认双方的接收能力和发送能力是否正常、指定自己的初始化序列号为后面的可靠性传送做准备。
第一次握手:客户端给服务端发一个 SYN 报文,并指明客户端的初始化序列号 ISN首部的同步位SYN=1,初始序号seq=x,SYN=1的报文段不能携带数据,但要消耗掉一个序号此时客户端处于 SYN_SENT 状态
第二次握手:服务器收到客户端的 SYN 报文之后,会以自己的 SYN 报文作为应答,并且也是指定了自己的初始化序列号 ISN(s)。同时会把客户端的 ISN + 1 作为ACK 的值,表示自己已经收到了客户端的 SYN此时服务器处于 SYN_RCVD 的状态
第三次握手:客户端收到 SYN 报文之后,会发送一个 ACK 报文,当然,也是一样把服务器的 ISN + 1 作为 ACK 的值,表示已经收到了服务端的 SYN 报文,此时客户端处于 ESTABLISHED 状态
服务器收到 ACK 报文之后,也处于 ESTABLISHED 状态,此时,双方已建立起了连接。
1.11为什么进行三次握手
第一次握手:客户端发送网络包,服务端收到了。
这样服务端就能得出结论:客户端的发送能力、服务端的接收能力是正常的。
第二次握手:服务端发包,客户端收到了。
这样客户端就能得出结论:服务端的接收、发送能力,客户端的接收、发送能力是正常的。不过此时服务器并不能确认客户端的接收能力是否正常。
第三次握手:客户端发包,服务端收到了。
这样服务端就能得出结论:客户端的接收、发送能力正常,服务器自己的发送、接收能力也正常。
如果是两次握手,会造成:
如客户端发出连接请求,但因连接请求报文丢失而未收到确认,于是客户端再重传一次连接请求。后来收到了确认,建立了连接。数据传输完毕后,就释放了连接,客户端共发出了两个连接请求报文段,其中第一个丢失,第二个到达了服务端,但是第一个丢失的报文段只是在某些网络结点长时间滞留了,延误到连接释放以后的某个时间才到达服务端,此时服务端误认为客户端又发出一次新的连接请求,于是就向客户端发出确认报文段,同意建立连接,
不采用三次握手,只要服务端发出确认,就建立新的连接了,此时客户端忽略服务端发来的确认,也不发送数据,则服务端一致等待客户端发送数据,浪费资源
1.12半连接状态
服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列。
1.13四次挥手

建立一个连接需要三次握手,而终止一个连接要经过四次挥手(也有将四次挥手叫做四次握手的)。这由TCP的半关闭(half-close)造成的。所谓的半关闭,其实就是TCP提供了连接的一端在结束它的发送后还能接收来自另一端数据的能力
第一次挥手:客户端发送一个 FIN 报文,报文中会指定一个序列号。此时客户端处于 FIN_WAIT1 状态。
即发出连接释放报文段(FIN=1,序号seq=u),并停止再发送数据,主动关闭TCP连接,进入FIN_WAIT1(终止等待1)状态,等待服务端的确认。
第二次挥手:服务端收到 FIN 之后,会发送 ACK 报文,且把客户端的序列号值 +1 作为 ACK 报文的序列号值,表明已经收到客户端的报文了,此时服务端处于 CLOSE_WAIT 状态。
即服务端收到连接释放报文段后即发出确认报文段(ACK=1,确认号ack=u+1,序号seq=v),服务端进入CLOSE_WAIT(关闭等待)状态,此时的TCP处于半关闭状态,客户端到服务端的连接释放。客户端收到服务端的确认后,进入FIN_WAIT2(终止等待2)状态,等待服务端发出的连接释放报文段。
第三次挥手:如果服务端也想断开连接了,和客户端的第一次挥手一样,发给 FIN 报文,且指定一个序列号。此时服务端处于 LAST_ACK 的状态。
即服务端没有要向客户端发出的数据,服务端发出连接释放报文段(FIN=1,ACK=1,序号seq=w,确认号ack=u+1),服务端进入LAST_ACK(最后确认)状态,等待客户端的确认
。
第四次挥手:客户端收到 FIN 之后,一样发送一个 ACK 报文作为应答,且把服务端的序列号值 +1 作为自己 ACK 报文的序列号值,此时客户端处于 TIME_WAIT 状态。需要过一阵子以确保服务端收到自己的 ACK 报文之后才会进入 CLOSED 状态,服务端收到 ACK 报文之后,就处于关闭连接了,处于 CLOSED 状态。
即客户端收到服务端的连接释放报文段后,对此发出确认报文段(ACK=1,seq=u+1,ack=w+1),客户端进入TIME_WAIT(时间等待)状态。此时TCP未释放掉,需要经过时间等待计时器设置的时间2MSL后,客户端才进入CLOSED状态。
客户端执行主动关闭并进入TIME_WAIT是正常的,服务端通常执行被动关闭,不会进入TIME_WAIT状态。
1.14为什么需要四次关闭
因为当服务端收到客户端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当服务端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉客户端,“你发的FIN报文我收到了”。只有等到我服务端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四次挥手。
1.15TCP保证可靠性
- 序列号、确认应答、超时重传;数据到达接收方,接收方需要发出一个确认应答,表示收到该数据段,并且确认序列号会说明下次接收的数据序列号。如果发送方迟迟未收到确认应答,那么可能是发送数据丢失,也可能是确认应答丢失,这时发送方会等待一定时间后重传
- 窗口控制与高速重发控制/快速重传(重复确认应答);TCP利用窗口控制来提高传输速度,意思是在一个窗口大小内,不一定等到应答才能发送下一段数据,窗口大小就是无需等待确认而可以继续发送数据的最大值。
- 拥塞控制;如果窗口定义的很大,发送端连续发送大量的数据,可能会造成网络的拥堵,甚至网络瘫痪。所以TCP为了防止这种情况而进行了拥塞控制
1.16HTTP和HTTPS的区别,以及HTTPS的优缺点
区别:
(1)HTTP协议是以明文的方式在网络中传输数据,HTTPS协议传输是数据时经过TLS加密后的,HTTPS具有更高的安全性。(2)HTTPS在TCP三次握手之后还要进行SSL的握手。(3)HTTPS协议需要服务器申请证书,客户端安装对应的根证书。(4)HTTP协议端口号是80,HTTPS端口号是443。
HTTPS优点:
(1)传输过程中使用密钥加密,安全性更高。(2)HTTPS可以认证用户和服务器,确保数据发送到正确的用户和服务器。
HTTPS缺点:
(1)HTTPS握手阶段延时较高(因为三次握手之后还有SSL握手)。(2)HTTPS部署成本高:一方面HTTPS协议需要使用证书来验证自身安全性,所以需要购买CA证书;另一方面再用HTTPS协议需要进行加密解密的计算,占用CPU资源较多,需要的服务器配置或者数目增加。
1.17HTTP返回码
1XX:提示信息–表示请求已经接收,继续处理
2XX:成功–表示请求已被成功接收、理解、接受
3XX:重定向–要完成请求必须进行更进一步操作
4XX:客户端错误–请求有错误或者请求无法实现
5XX:这些状态代码表示服务器在尝试处理请求时发⽣内部错误
200 (成功) 服务器已成功处理了请求。 通 常,这表示服务器提供了请求的⽹⻚。
201 (已创建) 请求成功并且服务器创建了新的资源。
300 (多种选择) 针对请求,服务器可执⾏多种操作。 服务器可根据请求者 (user agent) 选择⼀项操作,或 提供操作列表供请求者选择。
301 (永久移动) 请求的⽹⻚已永久移动到新位置。 服务器返回此响应 (对 GET 或 HEAD 请求的响应)时,会⾃动将请求者转到新位置。
302 (临时移动) 服务器⽬前从 不同位置的⽹⻚响应请求,但请求者应继续使⽤原有位置来进⾏以后的请求。304 (未修改) ⾃从上次 请求后,请求的⽹⻚未修改过。 服务器返回此响应时,不会返回⽹⻚内容。
400 (错误请求) 服务器不理解请求的语法。
401 (未授权) 请求要求身份验证。 对于需要登录的⽹⻚,服务器可能返回此响应。
403 (禁⽌) 服务器拒绝请求。
404 (未找到) 服务器找不到请求的⽹⻚。
405 (⽅法禁⽤) 禁⽤请求中指定的⽅ 法。
500 (服务器内部错 误) 服务器遇到错误,⽆法完成请求。
502 (错误⽹关) 服务器作为⽹关或代理,从上游服务器收到 ⽆效响应。
504 (⽹关超时) 服务器作为⽹关或代理,但是没有及时从上游服务器收到请求。
505 (HTTP 版本不受⽀持) 服务器不⽀持请求中所⽤的 HTTP 协议版本
用户从输入URL到显示页面整个过程
DNS解析–>TCP连接–>发送HTTP请求–>服务器处理请求并返回HTTP报文–>浏览器解析渲染页面–>连接结束
1.18GET和POST的区别
(1)概括:对于GET方式请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);
而对于POST方式请求,浏览器会先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)。
(2)区别:
1)get参数通过url传递,post放在request body中。
2)get请求在URL中传递参数的长度有限制(根据浏览器不同长度限制也不同),而post没有
3)get比post更不安全,因为参数暴露在URL中
4)get请求只能进行url编码,post支持多种编码方式
5)get 请求会被浏览器主动cache (缓存),post 则不会,除非手动设置
6)get请求会完整保留在浏览历史记录里,而post中的参数不会被保留
7)get和post本质上是TCP连接,并无差别,但是由于http的规定和浏览器/服务器的限制,导致他们在应用过程中体现出一些不同
8)GET产生一个TCP数据包,post产生两个数据包。
9)一般get请求用来获取数据,post请求用来发送数据。
本文涵盖了软件开发中的关键概念,包括但不限于单例模式、数据库查询优化、线程安全、网络通信原理等,深入剖析了这些技术的基本原理及实际应用场景。
1174

被折叠的 条评论
为什么被折叠?



