TCP协议 TCP 相比于 UDP 具有有连接、可靠传输、面向字节流等特性,这注定了 TCP 协议要比 UDP 协议复杂的多。♫TCP协议段格式首先,与 UDP 协议不同的一点就是,UDP 首部长度是固定为8字节的,而 TCP 首部中有个选项部分未指定其具体大小,所以 TDP 的首部长度是不确定的;此外,TCP 的首部长度单位是4字节,而除选项外的首部长度固定是20字节,故可以通过首部长度-20字节得到选项长度;
UDP协议 由于只有两个字节大小,这说明 UDP 报文的最大长度不超过 2^16 = 65535 个字节(64KB)大小,即接收的应用层数据载荷大小不能超过64KB大小,若要传输大于64KB大小的数据,通常的做法是将数据分割成多个小于或等于64KB的数据包进行发送或者使用其他传输层协议(如:TCP 协议)。接收方收到数据包后,会重新计算校验和并与报头中的校验和进行比较。由上图可知,UDP 报头长度一共为8字节(32个比特位),其中源端口、目的端口、UDP 长度、UDP 校验和分别是2字节(16个比特位)大小。
网络通信流程 ♫IP地址我们已经知道 IP 地址由网络号主机号组成,根据IP 地址的不同可以有5钟划分网络号和主机号的方案其中,各类地址的表示范围是:分类范围适用网络网络数量主机最大连接数上面 A 类、B 类、C 类的 IP 地址由网络号标识不同的广域网网段,而若想在广域网里再划分子网,就要通过子网掩码对 IP 地址进行子网划分。♫子网掩码子网掩码是一个32位地址,由连续的1和0组成,连续的1表示网络号,连续的0表示主机号,通过0的个数可以计算出子网中主机的IP地址范围。
网络编程基础 ♫什么是网络编程网络编程,指网络上的主机,通过不同的进程,以编程的方式实现网络数据传输。在网络编程中,获取一个网络资源,涉及到两次网络数据传输: 发送端请求数据的发送 和 接受端响应数据的发送,其中提供服务的一端叫做接受端,获取服务的一端叫做客户端。
网络的一些基本概念 ♫网络发展历程♪独立模式计算机之间相互独立♪网络互连随着时代的发展,越来越需要计算机之间互相通信,共享软件和数据,即以多个计算机协同工作来完成业务,就有了网络互连。网络互连是指将多台计算机连接在一起,完成数据共享。数据共享本质是网络数据传输,即计算机之间通过网络来传输数据,也称为网络通信。根据网络互连的规模不同,可以划分为局域网和广域网。♪局域网局域网,即 Local Area Network,简称LAN。
简单了解 JVM ♫什么是JVMJVM 是 Java Virtual Machine 的简称,意为 Java虚拟机。虚拟机是指通过软件模拟的具有完整硬件功能的、运行在一个完全隔离的环境中的完整计算机系统(如:JVM、VMwave、Virtual Box)。JVM 和其他两个虚拟机的区别是: VMwave与VirtualBox是通过软件模拟物理CPU的指令集,物理系统中会有很多的寄存器,而 JVM则是通过软件模拟Java字节码的指令集,JVM中只是主要保留了PC寄存器,其他的寄存器都进行了裁剪。♫JVM的运行流程。
文件操作--IO Java中使用流对象对文件内容进行操作,从类型上分为以字节为单位读写数据的字节流对象(InputStream、OutputStream)和以字符为单位读写数据的字符流对象(Reader、Writer),流对象的四个核心操作是:①.打开文件(构造方法)(8)②.关闭文件(close())③.读文件(read() 对应 InputStream 和 Reader)④.写文件(write() 对应 OutPutStream 和 Writer)。常见的文本文件格式包括.txt、.html、.xml 等。
JUC(Java.util.concurrent)的常见类 添加完元素之后,再将原容器的引用指向新的容器。例如:图书馆有500个空位,学生扫码入座,可用座位-1(相当于信号量的P操作),学生扫码离座,可用座位+1(相当于信号量的V操作),如果座位为0,就无法再扫码入座了(阻塞等待)。CopyOnWrite容器即写时拷贝的容器,当我们往一个容器添加元素的时候,不直接往当前容器添加,而是先将当前容器进行拷贝, 复制出一个新的容器,然后在新的容器里添加元素,ConcurrentHashMap优化了扩容方式,即化整为零:发现需要扩容的线程,只需要创建一个新的数组,
synchronized的原理和Callable接口 偏向锁并不是真正的加锁,只是给线程一个偏向锁的标记,如果一直没有其他线程尝试加锁,则等到synchronized执行完取消偏向锁标记即可,如果有其他线程尝试加锁,则再真在进行加锁,让其它线程进入阻塞等待。这里的重量级锁就是使用OS提供的 mutex 锁,此时其它线程尝试加锁就是在内核态来判断锁是否被占用,如果没被占用则返回用户态,否则就进入等待队列,等待操作系统唤醒。锁消除的主要依据是:如果程序中某些代码块不会被多个线程同时访问,那么这些代码块中的锁就可以被消除,因为不需要花费额外的时间来进行同步操作。
锁策略与CAS 当一个值从A变成B再变成A时,如果在这个时间间隔内,有另外一个线程对这个值进行了改变并且恰好从A变成了另外一个值C,那么CAS操作会误认为这个值没有被修改过,从而可能导致并发异常,这就是CAS的ABA问题。以AtomicInteger为例,CAS实现原子类的原理是在操作值的时候,先读取当前的值,然后对比当前值是否与期望的值相同,如果相同,则执行操作并更新值;支持重复加锁和释放的锁,同一个线程可以多次获得同一个锁,如果线程已经持有该锁,则需要进行重入计数,多少次加锁就需要多少次释放锁才能真正释放。
线程池与工厂模式 线程池的优点是可以避免频繁地创建和销毁线程带来的性能开销,通过线程池获取线程和归还线程通过代码就能实现,相比于直接通过操作系统内核来获取和销毁线程来说开销要小很多。而叫舍友买,舍友可能先去干其它事情再去买,整体行为是不可控的)此外,线程池还可以控制应用程序中的并发线程数,避免因线程过多而造成系统资源的浪费和线程调度的开销。空闲线程的存活时间,即如果线程池中的线程数量大于 corePoolSize,那么多余的线程在空闲一定时间后将被销毁,直到线程池中的线程数重新达到 corePoolSize 为止。
定时器和Timer 此时虽然解决了“忙等”的问题,但还有一个线程安全的问题:如果线程1运行到 this.wait(myTask.getTime() - time) 之前刚好被调度去其他线程执行添加新的任务操作,之后再调度到线程1执行wait操作,即先执行了 schedule() 里的 notify 后再执行构造方法里的 wait ,此时如果新添加的任务的执行时间更靠前仍不能提前唤醒线程1,就会有问题。毫秒时间戳来描述任务执行的时刻),由于这个对象需要放到优先队列中,第一个参数指定即将要执行的任务代码,schedule 方法,
阻塞队列. ♫什么是阻塞队列阻塞队列是一种特殊的队列,它除了具备队列的先进先出的特点外,还具有以下特点:♩.如果队列为空时,执行出队列操作,会阻塞等待,直到另一个线程往队列里添加元素(队列不为空)♩.如果队列满了时,执行入队列操作,会阻塞等待,直到另一个线程取出队列里的元素(队列没有满)阻塞队列常用于实现生产者-消费者模式,通过控制队列的阻塞,使得生产者和消费者的协作更加有效和高效。下面我们先简单认识下生产者-消费者模式~♫什么是生产-消费者模式生产者消费者模式是一种常见的并发编程模式。
单例模式. 通过将构造方法设置为私有的,保证类外无法通过new来创建实例的同时,通过static将uniqueInstance成员属性修饰为类属性(Java代码中的每个类在编译完成后都会生成.class文件,JVM加载时通过读取.class文件中的二进制指令来在内存中构造出类对象(Singleton.class),类对象的属性就是类属性),由于类对象只有一份,故类属性也就只有一份。单例模式是一种设计模式,通过巧用Java的现有语法,实现一个只能被创建一个实例的类,并提供一个全局访问点。♫单例模式的线程安全问题。
线程间的调度顺序 但由于线程的随机调度,并不能保证t1线程比t2线程先执行,如果是t2线程先执行的话,notify先执行,那么notify相当于是无效通知,等到t1线程调用wait方法后就没有对应的notify唤醒了。注:虽然wait(500)与sleep(500)很像,但wait(500)通过notify或notifyAll唤醒不会抛出异常,属于正常的代码,而sleep(500)虽然能通过interrupt唤醒,但是却是以异常的形式唤醒的,属于出问题的代码。可以让一个线程等待令另一个线程执行完毕/一定时间,
线程安全问题 synchronized是可重入锁,即一个线程可以对同一个对象反复加锁(在可重入锁的内部包含了 "线程持有者" 和 "计数器" 两个信息,如果某个线程加锁的时候发现锁已经被人占用,但是恰好占用的正是自己,那么仍然可以继续获取到锁,并让计数器自增,解锁的时候计数器递减为 0 的时候,才真正释放锁)加了synchronized之后,若获取不到锁,则会一直阻塞等待,直到获取锁为止;线程1拿到了锁A,线程2拿到了锁B,线程1再尝试获取锁B,线程2再尝试获取锁A,线程1等待线程二释放锁B,线程2等待线程1释放锁A。
多线程编程 线程可以看作是进程中的一个实体,一个进程可以包含一个或多个线程,这些线程共享进程的同一份资源(主要指内存(一个线程new的对象其他线程也能用)和文件描述符表(一个线程打开的文件其他线程也能用)),但是每个线程都拥有自己的栈空间和局部变量,都可以执行各自的上下文,且不会相互影响。start()的作用是调用系统的API,通过操作系统内核创建新的线程,并把要执行的指令交给新线程,当新线程调度到CPU上执行是,就执行到run()方法。需要记住一点:JVM会在一个进程的所有非守护线程结束后,才会结束运行。