技术问答集锦(四)

781 阅读20分钟

2 TCP 与 UDP

  1. 网络层:IP协议、ICMP协议、ARP协议、RARP协议和BOOTP协议;
  2. 传输层:TCP协议与UDP协议;
  3. 应用层:FTP、HTTP、TELNET、SMTP、DNS等协议;

HTTP是应用层协议,其传输都是被包装成TCP协议传输。可以用SOCKET实现HTTP。

Socket是一组接口,可以实现TCP,也可以实现UDP。在设计模式中,Socket其实就是一个门面模式,它把复杂的TCP/UDP协议族隐藏在Socket接口后面,对用户来说,一组简单的接口就是全部,让Socket去组织数据,以符合指定的协议。

网络协议结构

  1. TCP --- 传输控制协议,提供的是面向连接、可靠的字节流服务。当客户和服务器彼此交换数据前,必须先在双方之间建立一个TCP连接,之后才能传输数据。TCP提供超时重发,丢弃重复数据,检验数据,流量控制等功能,保证数据能从一端传到另一端。 理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接前,TCP 连接都将被一直保持下去。断开连接时服务器和客户端均可以主动发起断开TCP连接的请求。

  2. UDP --- 用户数据报协议,是一个无连接的简单的面向数据报的运输层协议。UDP不提供可靠性,它只是把应用程序传给IP层的数据包发送出去,但是并不能保证它们能到达目的地。由于UDP在传输数据包前不用在客户和服务器之间建立一个连接,且没有超时重发等机制,故而传输速度很快。

  3. TCP连接的三次握手:要了解TCP,一定要知道 "三次握手,四次拜拜" 所谓的三次握手,就是发送数据前必须建立的连接叫三次握手,握手完了才开始发的,这也就是面向连接的意思。

  1. 第一次握手:客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状态,等待服务器确认;
  2. 第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
  3. 第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。

3 Java Thread

程序:是一段静态代码,它是应用软件执行的蓝本。

进程:是程序运行时的一个实例,对应了从代码加载、执行的一个完整过程,此过程也对应进程产生、发展到消亡的过程。

线程:比进程更小的执行单位,一个进程在其执行过程中,可以产生多个执行线程,即每个线程也有它的产生、发展到消亡的过程。是一个动态概念。

  1. 粒度层次不同,是两个不同层次上的概念。进程是由操作系统来管理的,而线程则是由进程来管理。
  2. 共享资源不同,不同进程的代码、内部数据和状态都是完全独立的,而一个进程内的多线程是共享进程空间资源的,有可能互相影响。
  3. 切换效率不同,线程本身的数据通常只有寄存器数据,以及一个进程执行时使用的堆栈,所以线程比进程切换负担小。

3.1 Thread生命周期及状态模型

线程生命周期

3.2 Thread 常用方法

  1. Thread:

public static Thread currentThread( ):返回当前线程对象,是一个静态的方法。

public static void sleep( long millis):使当前线程进入睡眠状态,参数设定其等待时间,不会释放锁,静态方法。

public static void yield():使当前线程放弃执行,切换到其它线程,是一个静态方法。

  1. Thread Instance:

public void start():启动线程,JVM将调用此线程的run方法,结果是将同时运行两个线程,当前线程和执行run方法的线程。

public void run():Thread的子类应该重写此方法,内容应为该线程应执行的任务。

public void stop():停止线程运行,并退出可执行状态。 【已过时】

public void resume():将暂停的线程继续执行。【已过时】

public void suspend():使线程暂停执行,不退出可执行态。【已过时】

public void interrupt():中断线程。

public void join():在当前线程中加入调用join方法的线程A,直到线程A死亡才能继续执行当前线程。

public void join(long millis):在当前线程中加入调用join方法的线程A,直到到达参数指定的毫秒数或线程A死亡才能继续执行当前线程。

public void setPriority(int newPriority):设置线程优先级。

public void setDaemon(boolean on):设置是否为后台线程。如果当前运行线程均为后台线程则JVM停止运行。该方法必须在start()方法之前使用。

public final void checkAccess():判断当前线程是否有权力修改调用此方法的线程。

public boolean isAlive():判断线程是否处于执行状态。返回值true表示处于运行状态,false表示已停止。

  1. Object:

public void wait():在其他线程调用此对象的notify()方法或notifyAll() 方法前,使当前线程进入等待状态,会释放锁。

public void notify(): 唤醒在此对象监视器上等待的单个线程。

public void notifyAll():唤醒在此对象监视器上等待的所有线程。

3.3 main() 主线程与子线程

  1. 主线程:Java应用程序总是从主类的main()方法开始执行。当JVM加载代码,发现main方法之后,启动的线程称作“主线程”,该线程负责执行main方法。
  2. 子线程:在main方法的执行中再创建的线程。

如果main方法中又创建了子线程,那么JVM就要在主线程和子线程之间轮流切换,main方法即使执行完最后的语句,JVM也不会结束程序,JVM一直要等到程序中的所有线程都结束之后,才结束我们的Java应用程序

3.4 Thread 线程创建两种方法

  1. Thread类:Thread类的子类创建线程对象,子类重写Thread类中的run()方法

    在Java程序中创建多线程的方法之一是继承Thread类,从Thread类派生一个子类,并创建这个子类的对象,就可以产生一个新的线程。这个子类应该重写Thread类的run方法,在run方法中写入需要在新线程中执行的语句段。这个子类的对象需要调用start方法来启动,新线程等待CPU调度将自动进入run方法。当前线程将同时继续往下执行。

    当创建子类的多个线程对象时,其成员变量和run()方法中局部变量都是互不干扰的

  2. Runnable接口:使用Thread类直接创建线程对象,但需要传入实现Runable接口的目标对象

    在编写复杂程序时相关的类可能已经继承了某个基类,而Java不支持多继承,在这种情况下,便需要通过实现Runnable接口来生成多线程。

    使用Thread创建线程对象时,通常使用的构造方法是:Thread(Runnable target),该构造方法中的参数是一个Runnable类型的接口,因此,在创建线程对象时必须向构造方法的参数传递一个实现Runnable接口类的实例,该实例对象称作所创线程的目标对象。

    当线程调用start方法后,一旦轮到它来享用CPU资源,目标对象就会自动调用接口中的run方法(接口回调)。

    对于使用同一目标对象的线程,目标对象的成员变量自然就是这些线程的共享数据单元。不同run()方法中的局部变量互不干扰

3.5 Thread 同步

synchronized —— 线程同步关键字

把需要修改数据的方法用关键字synchronized来修饰,用于指定需要同步的代码段或方法,也就是监视区。

当一个线程A使用一个synchronized修饰的方法时,其他线程想使用这个方法时就必须等待,直到线程A 使用完该方法 (除非线程A使用wait主动让出CUP资源)。

当被synchronized限定的代码段执行完,就释放对象锁(信号量)。

3.6 Thread 通信

为了更有效地协调不同线程的工作,需要在线程间建立沟通渠道,通过线程间的“对话”来解决线程间的同步问题。

java.lang.Object 类的一些方法为线程间的通讯提供了有效手段。

一个线程在使用的同步方法中时,可能根据问题的需要,必须使用wait() (挂起)方法使本线程等待,暂时让出CPU的使用权,并允许其它线程使用这个同步方法。其它线程如果在使用这个同步方法时如果不需要等待,那么它用完这个同步方法的同时,应当执行notify(), notifyAll()(恢复)方法通知所有的由于使用这个同步方法而处于等待的线程结束等待

3.7 Thread 调度机制

每个Java线程都有一个优先级,其范围都在1和10之间。 默认情况下,每个线程的优先级都设置为5

在线程A运行过程中创建的新的线程对象B,初始状态具有和线程A相同的优先级。可在线程创建之后的任何时候,通过setPriority(int priority)方法改变其原来的优先级。

基于线程优先级的线程调度:

  1. 具有较高优先级的线程比优先级较低的线程优先执行;
  2. 对具有相同优先级的线程,Java的处理是随机的;
  3. 底层操作系统支持的优先级可能要少于10个,这样会造成一些混乱。因此,只能将优先级作为一种很粗略的工具使用。最后的控制可以通过明智地使用yield()函数来完成;

假设某线程正在运行,则只有出现以下情况之一,才会使其暂停运行:

  1. 一个具有更高优先级的线程变为就绪状态(Ready);
  2. 由于输入/输出(或其他一些原因)、调用sleep、wait、yield方法使其发生阻塞;
  3. 对于支持时间分片的系统,时间片的时间期满;

通常,我们在一个线程内部插入yield()语句,这个方法会使正在运行的线程暂时放弃执行,这时具有同样优先级的线程就有机会获得调度开始运行,但较低优先级的线程仍将被忽略不参加调度;

3.8 Thread 挂起与恢复

挂起 有时候两个线程并不是同步的,即不涉及都需要调用一个同步方法,但线程也可能需要暂时的挂起。所谓挂起一个线程就是让线程暂时让出CPU的使用权限,暂时停止执行,但停止执行的持续时间不确定,因此不能使用sleep方法暂停线程。挂起一个线程需使用wait方法,即让准备挂起的线程调用 wait 方法,主动让出CPU的使用权限

恢复 为了恢复该线程,其它线程在占有CUP资源期间,让挂起的线程的目标对象执行notifyAll()方法,使得挂起的线程继续执行;如果线程没有目标对象,为了恢复该线程,其它线程在占有CPU资源期间,让挂起的线程调用notifyAll()方法,使挂起的线程继续执行。

4 Java数据结构

  1. 线性表结构一般分为三种:顺序线性表、单链表、双链表;特征:

    1. 一个特定的线性表,应该是用来存放特定的某一个类型的元素的( 元素的“同一性”);
    2. 除第一个元素外,其他每一个元素 有且仅有一个直接前驱;除最后一个元素外,其他每一个元素 有且仅有一个直接后继元素的“序偶性”);
    3. 元素在线性表中的“下标” 唯一地确定该元素在表中的相对位置( 元素的“索引性”)
  2. 二叉树是树形结构的一个重要类型。许多实际问题抽象出来的数据结构往往是二叉树的形式,即使是一般的树也能简单地转换为二叉树,而且二叉树的存储结构及其算法都较为简单,因此二叉树显得特别重要。

    二叉树(BinaryTree)是n(n≥0)个结点的有限集,它或者是空集(n=0),或者由一个根结点及两棵互不相交的、分别称作这个根的左子树和右子树的二叉树组成。这个定义是递归的。由于左、右子树也是二叉树, 因此子树也可为空树。

    二叉树遍历:由于被访问的结点必是某子树的根,所以N(Node)、L(Left subtlee)和R(Right subtree)又可解释为根、根的左子树和根的右子树。NLR、LNR和LRN分别又称为 先根遍历、中根遍历和后根遍历

  3. 队列是一个常用的数据结构,是一种 先进先出(First In First Out, FIFO) 的结构,也就是说只能在表头进行删除,在表尾进行添加。

  4. 栈是一种 后进先出(Last In First Out,LIFO) 的数据结构。

5 JDK命令行工具

jps : 只用于列出java的进程。

jstat :可用于观察Java应用程序运行时信息的工具。

jinfo :可用于查看正在运行的Java应用程序的扩展参数,甚至支持在运行时修改部分参数。

jmap :生成Java应用程序的堆快照和对象的统计信息。jmap -histo 2972 > heap.log

jhat :用于分析Java应用程序的堆快照内容文件。jhat heap.log

jstack : 用于导出Java应用程序的线程堆栈。 jstack -l 2348 > jstack.log

6 静态代理和动态代理

  1. 静态代理:由程序员创建或工具生成代理类的源码,再编译代理类。所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在编译期就确定了

  2. 动态代理:动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行期确定

7 Http基础

7.1 HTTP协议

一种通信协议,它允许将超文本标记语言(HTML)文档从Web服务器传送到客户端的浏览器。 一个属于应用层的面向对象的协议,由于其简捷、快速的方式,适用于分布式超媒体信息系统。

HTTP协议的主要特点如下:

  1. 支持客户/服务器模式,基于TCP实现;
  2. 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。请求方法常用的有GET、POST、DELETE、PUT。每种方法规定了客户与服务器联系的类型不同。由于HTTP协议简单,使得HTTP服务器的程序规模小,因而通信速度很快;
  3. 灵活:HTTP允许传输任意类型的数据对象。正在 传输的类型由 Content-Type 加以标记
  4. 短连接:短连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  5. 无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

7.2 CGI是什么

CGI(Common Gateway Interface),通用网关接口,它是一段程序,运行在服务器上如:HTTP服务器,提供同客户端HTML页面的接口。利用程序的标准输入输出流,完成HTTP通信。HTTP 是文本协议,每次请求的文本以标准输入流的形式进入服务器端 CGI 程序,创建进程;然后进程的标准输出流作为响应

CGI程序可以是Python脚本,PERL脚本,SHELL脚本,C或者C++程序等。 所有服务器端 HTTP 处理都是类 CGI 的原理,接受符合协议的标准输入文本流,从标准输出流输出同样符合协议的文本,也就是“请求”和“响应”

7.3 负载均衡、分布式集群

在负载均衡的思路下,多台服务器为对等方式,每台服务器都具有同等的地位,可以单独对外提供服务而无须其他服务器的辅助。通过负载分担技术,将外部发送来的请求按一定规则分配到对称结构中的某一台服务器上,而接收到请求的服务器都独立回应客户机的请求。

常用负载均衡技术:反向代理负载均衡 (如Apache+JK2+Tomcat这种组合),使用代理服务器可以将请求转发给内部的Web服务器,让代理服务器将请求均匀地转发给多台内部Web服务器之一上,从而达到负载均衡的目的。这种代理方式与普通的代理方式有所不同,标准代理方式是客户使用代理访问多个外部Web服务器,而这种代理方式是多个客户使用它访问内部Web服务器,因此也被称为反向代理模式。

客户系统一般采用Apache httpd作为web服务器,即作为Tomcat的前端处理器,根据具体情况而定,有些情况下是不需要Apache httpd作为 web 服务器的,如系统展现没有静态页面那就不需要Apache httpd,那时可以直接使用Tomcat作为web服务器来使用。使用Apache httpd主要是它在处理静态页面方面的能力比Tomcat强多了。

会话亲和:就是表示来自同会话的所有请求都由相同的 Tomcat 实例来处理,这种情况下,如果Tomcat实例或所执行的服务器机器失效,也会丧失Servlet的会话数据。即使在集群系统中执行更多的Tomcat实 例,也永远不会复制会话数据。这样是提高集群性能的一种方案,但不具备有容错能力了。

会话复制:只配置负载均衡还不行,还要 session 复制,也就是说其中任何一个 tomcat 的添加的 session ,是要同步复制到其它 tomcat , 集群内的 tomcat 都有相同的 session,使用会话复制,则当一个Tomcat实例宕掉时,由于至少还有另一个Tomcat实例保有一份会话状态数据,因而数据不会丧失。但性能会 有所降低。

其实无论是分布式,数据缓存,还是负载均衡,无非就是改善网站的性能瓶颈,在网站源码不做优化的情况下,负载均衡可以说是最直接的手段了。其实抛开这个名词,放开了说,就是希望用户能够分流,也就是说把所有用户的访问压力分散到多台服务器上,也可以分散到多个 tomcat里,如果一台服务器装多个tomcat,那么即使是负载均衡,性能也提高不了太多,不过可以提高稳定性,即容错性。当其中一个主tomcat 当掉,其他的tomcat也可以补上,因为tomcat之间实现了Session共享。待tomcat服务器修复后再次启动,就会自动拷贝所有 session数据,然后加入集群。这样就可以不间断的提供服务。如果要真正从本质上提升性能,必须要分布到多台服务器。

8 Linux基础

8.1 Linux启动原理

CMOS:记录 各项硬件参数且嵌入在主板上面的存储器; BIOS:一个写入到主板上的一个韧体(韧体就是写入到硬件上的一个程序)。BIOS就是在开机的时候计算机系统会主动执行的第一个程序了。

  1. 首先加载BIOS程序,通过BIOS程序去 加载CMOS信息,取得主机的各项硬件配置,并开始进行开机自检,依据设置取得第一个可启动的设备
  2. 加载执行第一个启动设备内MBR的Boot Loader
  3. 依据Boot Loader的 设置加载Kernel,Kernel会开始检测硬件与加载驱动程序;
  4. 在硬件驱动成功后, Kernel会主动调用init进程,而init会取得run-level信息;
  5. init执行 /etc/rc.d/rc.sysinit文件来准备软件执行的操作环境(如网络,时区等);
  6. init执行 run-level的各个服务启动(script方式);
  7. init执行 /etc/rc.d/rc.local文件;
  8. init执行 终端模拟程序mingetty来启动login进程,最后就等待用户登录;

8.2 Linux命令行工具

top实时显示系统中各个进程的资源占用状况。前半部分是系统统计信息,后半部分是进程占用资源状况信息。

sar周期性地对内存和CPU使用情况进行采样。查看CPU使用情况:sar -u 1 3;查看内存使用情况:sar -r 1 3;查看I/O使用情况:sar -b 1 3

vmstat : 周期性地统计CPU 内存 swap 使用情况等信息。vmstat 1 3

iostat : 周期性查看I/O信息。iostat 1 2

pidstat:功能强大的性能检测工具, 不仅可以监视进程的CPU,I/O,内存资源,也可以监视线程的性能情况

  1. 查看CPU使用情况: pidstat -p 1187 -u 1 3【-t 参数将系统性能的监控细化到线程级别】
  2. 查看内存使用情况: pidstat -p 1187 -r 1 3 【-t 参数将系统性能的监控细化到线程级别】
  3. 查看I/O使用情况: pidstat -p 1187 -d 1 3 【-t 参数将系统性能的监控细化到线程级别】

netstat用于查看各种网络相关信息,如网络连接,路由表,接口状态 (Interface Statistics),masquerade 连接,多播成员 (Multicast Memberships) 等等。

netstat -na 来显示所有连接的端口并用数字表示

mpstat用于获取 CPU 相关统计信息。mpstat -P ALL 5 2 显示了系统中 CPU 的各种统计信息。–P ALL 选项指示该命令显示所有 CPU 的统计信息。参数 5 2 指示该命令每隔 5 秒运行一次,共运行 2 次。

pmap:report memory map of a process(查看进程的内存映像信息)。

pmap -d 1

 -x   extended       Show the extended format. 显示扩展格式

 -d   device         Show the device format.   显示设备格式

 -q   quiet          Do not display some header/footer lines. 不显示头尾行

 -V   show version   Displays version of program. 显示版本