记录一下自己面试被问到的,持续更新
线程之间的通信方式有哪些?进程呢?
一、进程之间的通讯方式
- 管道(pipe)
管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常指父子进程 - 有名管道(namedpipe)
有名管道也是半双工的通信方式,但是他允许无亲缘关系进程间的通信 - 信号量(semophore )
信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。 - 信号 (sinal )
信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。 - 消息队列( messagequeue )
消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。 - 共享内存(shared memory )
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。 - 套接字(socket )
套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同设备及其间的进程通信。
二、线程间的通信
转自:博客
- 使用同一个共享变量控制
synchronized、wait、notify
Lock、Condition
利用volatile
利用AtomicInteger
Synchronized、wait、notify
利用volatile
volatile修饰的变量值直接存在主内存里面,子线程对该变量的读写直接写住内存,而不是像其它变量一样在local thread里面产生一份copy。volatile能保证所修饰的变量对于多个线程可见性,即只要被修改,其它线程读到的一定是最新的值。
利用AtomicInteger
和volatile类似
-
利用BlockingQueue
这里用流在两个线程间通信,但是Java中的Stream是单向的,所以在两个线程中分别建了一个input和output -
线程间的直接的数据交换:通过管道进行线程间通信PipedInputStream、PipedOutputStream,1)字节流;2)字符流
三、讲讲Mybatis的一级缓存和二级缓存
一级缓存
Mybatis的一级缓存是指Session缓存。一级缓存的作用域默认是一个SqlSession。Mybatis默认开启一级缓存。
也就是在同一个SqlSession中,执行相同的查询SQL,第一次会去数据库进行查询,并写到缓存中;
第二次以后是直接去缓存中取。
当执行SQL查询中间发生了增删改的操作,MyBatis会把SqlSession的缓存清空。
一级缓存的范围有SESSION和STATEMENT两种,默认是SESSION,如果不想使用一级缓存,可以把一级缓存的范围指定为STATEMENT,这样每次执行完一个Mapper中的语句后都会将一级缓存清除。
如果需要更改一级缓存的范围,可以在Mybatis的配置文件中,在下通过localCacheScope指定。
<setting name="localCacheScope" value="STATEMENT"/>
注意
当Mybatis整合Spring后,直接通过Spring注入Mapper的形式,如果不是在同一个事务中每个Mapper的每次查询操作都对应一个全新的SqlSession实例,这个时候就不会有一级缓存的命中,但是在同一个事务中时共用的是同一个SqlSession。
如有需要可以启用二级缓存。
二级缓存
Mybatis的二级缓存是指mapper映射文件。二级缓存的作用域是同一个namespace下的mapper映射文件内容,多个SqlSession共享。Mybatis需要手动设置启动二级缓存。
二级缓存是默认启用的(要生效需要对每个Mapper进行配置),如想取消,则可以通过Mybatis配置文件中的元素下的子元素来指定cacheEnabled为false。
<settings>
<setting name="cacheEnabled" value="false" />
</settings>
不同点:
-
一级缓存存在于一个SqlSession之内,二级缓存是mapper级别的缓存,存在于不同的SqlSession之间
-
一级缓存不需要手动开启,属于默认开启状态;二级缓存需要手动开启
相同点:
- 在增删改SQL之后,缓存会自动清空
- flushCache="true"的查询语句查询内容不存放进缓存
四、说一说三次握手
面试的时候让我画出三次握手的过程,还问了为什么是三次,两次,五次不行吗?这里总结一些三次握手和四次挥手的过程,以及分析一下为什么是三次
参考博客
三次握手:
-
第一次握手:Client将标志位SYN置为1,随机产生一个值seq=J,并将该数据包发送给Server,Client进入SYN_SENT状态,等待Server确认。
-
第二次握手:Server收到数据包后由标志位SYN=1知道Client请求建立连接,Server将标志位SYN和ACK都置为1,ack=J+1,随机产生一个值seq=K,并将该数据包发送给Client以确认连接请求,Server进入SYN_RCVD状态。
-
第三次握手:Client收到确认后,检查ack是否为J+1,ACK是否为1,如果正确则将标志位ACK置为1,ack=K+1,并将该数据包发送给Server,Server检查ack是否为K+1,ACK是否为1,如果正确则连接建立成功,Client和Server进入ESTABLISHED状态,完成三次握手,随后Client与Server之间可以开始传输数据了
四次挥手: -
第一次挥手:Client发送一个FIN,用来关闭Client到Server的数据传送,Client进入FIN_WAIT_1状态。
-
第二次挥手:Server收到FIN后,发送一个ACK给Client,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号),Server进入CLOSE_WAIT状态。此时TCP链接处于半关闭状态,即客户端已经没有要发送的数据了,但服务端若发送数据,则客户端仍要接收。
-
第三次挥手:Server发送一个FIN,用来关闭Server到Client的数据传送,Server进入LAST_ACK状态。
-
第四次挥手:Client收到FIN后,Client进入TIME_WAIT状态,接着发送一个ACK给Server,确认序号为收到序号+1,Server进入CLOSED状态,完成四次挥手。
问题:为什么是三次?
为了防止 已失效的链接请求报文突然又传送到了服务端,因而产生错误。
客户端发出的连接请求报文并未丢失,而是在某个网络节点长时间滞留了,以致延误到链接释放以后的某个时间才到达Server。这时,Server误以为这是Client发出的一个新的链接请求,于是就向客户端发送确认数据包,同意建立链接。若不采用“三次握手”,那么只要Server发出确认数据包,新的链接就建立了。由于client此时并未发出建立链接的请求,所以其不会理睬Server的确认,也不与Server通信;而这时Server一直在等待Client的请求,这样Server就白白浪费了一定的资源。若采用“三次握手”,在这种情况下,由于Server端没有收到来自客户端的确认,则就会知道Client并没有要求建立请求,就不会建立链接。
五、Tomcat的缺省端口号是多少,怎么修改,还占用了那些端口号
omcat默认的端口号是8080,还会占用8005,、8009和8443端口
进入tomcat安装目录,编辑文件”安装目录\apache-tomcat-7.0.6\conf\server.xml”
(即编辑tomcat目录下的conf目录下的server.xml文件)
六、SpringBoot的核心注解
看到主方法上只有一个注解**@SpringBootApplication**,这是一个复合注解,由多个注解组成,其中核心的三个注解分别是**@SpringBootConfiguration**、@ComponentScan和**@EnableAutoConfiguration**。
@SpringBootConfiguration
声明为配置类(根配置类,首先扫描该类,本身是一个IOC容器的配置类),将当前类内声明的一个或多个以@Bean注解标记的方法的实例纳入到spring容器中,并且实例名就是方法名。
SpringBoot使用Java Config技术进行配置,Java Config使用注解和Java代码的方式代替xml配置文件
任何一个标注了@Configuration的Java类定义都是一个JavaConfig配置类。
任何一个标注了@Bean的方法,其返回值将作为一个bean定义注册到Spring的IoC容器,方法名将默认成该bean定义的id
@ComponentScan
自动扫描当前包及子包下被@Component,@Controller,@Service,@Repository注解标记的类,将其作为bean加载到IOC容器中。
不指定范围则默认从注解所在类的包下进行扫描。
@EnableAutoConfiguration
核心注解,是自动配置的入口,SpringBoot根据添加的jar包来进行项目的默认配置