首页 > 技术文章 > 面试

laurarararararara 2020-03-30 13:23 原文

 

1.集合、多线程

集合框架:从类的继承结构来说,可以分为两大类,一类是继承自Collection接口,这类集合包含List、Set和Queue等集合类。另一类是继承自Map接口,这主要包含了哈希表相关的集合类。分为Collection和Map两种体系:

 

Collection接口:  set 元素无序、不可重复的集合(超市购物袋的东西)       List:元素有序、可重复的集合(班级成绩)  Queue:JUC包下阻塞队列的4个类,BlockingQueue 继承了Queue接口

Map接口:具有映射关系的"key-value对"的集合

 

LinkedBlockingQueue的容量是没有上限的(不指定时容量为Integer.MAX_VALUE,不要然的话在put时怎么会受阻呢),但是也可以选择指定其最大容量,它是基于单向链表的队列,此队列按 FIFO(先进先出)排序元素。

ArrayBlockingQueue在构造时需要指定容量, 并可以选择是否需要公平性,如果公平参数被设置true,等待时间最长的线程会优先得到处理(其实就是通过将ReentrantLock设置为true来 达到这种公平性的:即等待时间最长的线程会先操作)。通常,公平性会使你在性能上付出代价,只有在的确非常需要的时候再使用它。它是基于数组的阻塞循环队列,此队列按 FIFO(先进先出)原则对元素进行排序。

PriorityBlockingQueue是一个带优先级的 队列,而不是先进先出队列。元素按优先级顺序被移除,该队列也没有上限(看了一下源码,PriorityBlockingQueue是对 PriorityQueue的再次包装,是基于堆数据结构的,而PriorityQueue是没有容量限制的,与ArrayList一样,所以在优先阻塞队列上put时是不会受阻的。虽然此队列逻辑上是无界的,但是由于资源被耗尽,所以试图执行添加操作可能会导致 OutOfMemoryError),但是如果队列为空,那么取元素的操作take就会阻塞,所以它的检索操作take是受阻的。另外,往入该队列中的元 素要具有比较能力。

最后,DelayQueue(基于PriorityQueue来实现的,本质封装了PriorityQueue)是一个存放Delayed 元素的无界阻塞队列,只有在延迟期满时才能从中提取元素。如果延迟都还没有期满,则队列没有头部,并且poll将返回null。当一个元素的 getDelay(TimeUnit.NANOSECONDS) 方法返回一个小于或等于零的值时,则出现期满,poll就以移除这个元素了。此队列不允许使用 null 元素。

延时队列在项目中的应用还是比较多的,尤其像电商类平台:1、订单成功后,在30分钟内没有支付,自动取消订单2、外卖平台发送订餐通知,下单成功后60s给用户推送短信。3、如果订单一直处于某一个未完结状态时,及时处理关单,并退还库存

 我们在向DelayQueue队列中添加元素时,会给元素一个Delay(延迟时间)作为排序条件,队列中最小的元素会优先放在队首。队列中的元素只有到了Delay时间才允许从队列中取出。实现DelayQueue延时队列,队中元素要implements Delayed 接口,这哥接口里只有一个getDelay方法,用于设置延期时间。Order类中compareTo方法负责对队列中的元素进行排序。DelayQueueput方法是线程安全的,因为put方法内部使用了ReentrantLock锁进行线程同步。

Iterator 怎么使用?有什么特点?

Java中的Iterator功能比较简单,并且只能单向移动:

(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。

(2) 使用next()获得序列中的下一个元素。

(3) 使用hasNext()检查序列中是否还有元素。

(4) 使用remove()将迭代器新返回的元素删除。

Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素。

可能出现的问题:用遍历的时候要用迭代器进行增加删除,iterator.add/remove,如果用list集合调用方法会出现ConcurrentModificationException异常。

 

hashmap

1.7之前,数组+链表,基于hashing的原理,我们使用put(key, value)存储对象到HashMap中,使用get(key)从HashMap中获取对象。当我们给put()方法传递键和值时,我们先对键调用hashCode()方法,返回的hashCode用于找到bucket位置来储存Entry对象;数据节点是一个Entry节点(内部类),插入过程使用头插法,可能造成的问题:resize扩容方法会调用transfer方法,将里面的Entry进行rehash,在该过程可能会造成链表的循环,可能在下一步get的时候出现一个死循环的情况;也有可能没有加锁,多线程并发情况下,不能保证数据的安全性。1.7先判断是否需要扩容,再插入。

HashMap在并发执行put操作时会引起死循环,是因为多线程会导致HashMap的Entry链表 形成环形数据结构,一旦形成环形数据结构,Entry的next节点永远不为空,就会产生死循环获 取Entry

1.8变化,链表+数组+红黑树,原来的O(n)到O(logn),将原来的Entry节点变成Node节点,尾插法,put过程也有优化。扩容机制:初始化的时候,如果未设置capacity,默认容量16,负载因子0.75,会计算一个阈值threshold,put的时候会先判断,size是否大于阈值,如果大于,会2倍扩容。1.8线程也不安全,虽然尾插法不会改标链表循环的过程(假如两个线程同时进行put操作,刚好这两个线程的数据的hash都一样,并且该位置为null;假设A先进入后,还未进行数据插入时挂起,而B正常执行,正常插入数据,之后A又进行插入,而此时不会再进行判断,A会把B插入的数据覆盖,发生线程不安全);使用concurrenthashmap保证线程安全。1.8先插入,再判断是否扩容。

new map(1000),存入多少元素会出发扩容? 2的10次方1021,1024*0.75 =768存入第769个元素进行扩容。

put方法具体步骤:1.判断数组是否为空,为空进行初始化;2,不为空,计算key的hash值,通过(n-1)&hash计算当前的数组下标index;3.查看index号是否存在数据,没有就构造一个Node节点放在table[index];存在就说明发生了hash冲突,继续判断key是否相等,相等就替换;不相等就判断节点类型是否为树形节点,如果是,插入红黑树中;不是就插入普通链表中,判断链表长度是否大于8,大于就转化为红黑树。插入完成后判断当前节点数是否大于阈值,大于就开始扩容为原数组的2倍。

怎么设定初始化容量?默认16,负载因子0.75,如果自己传入初始大小为K,初始化的就为大于K的2的整数次方,如果传10,大小为16.

LinkedHashMap实现有序:内部维护了一个单链表,有头尾节点,before和after用于标识前置节点和后置节点,实现插入的顺序和访问顺序。

TreeMap实现有序:按照key的自然排序或者Comparator的顺序进行排序,内部通过红黑树实现。

hashset和hashmap:

*HashMap* *HashSet*
HashMap实现了Map接口 HashSet实现了Set接口
HashMap储存键值对 HashSet仅仅存储对象
使用put()方法将元素放入map中 使用add()方法将元素放入set中
HashMap中使用键对象来计算hashcode值 HashSet使用成员对象来计算hashcode值,对于两个对象来说hashcode可能相同,所以equals()方法用来判断对象的相等性,如果两个对象不同的话,那么返回false
HashMap比较快,因为是使用唯一的键来获取对象 HashSet较HashMap来说比较慢

Set元素如何存储保证不可重复性? 使用哈希算法。

哈希算法:当向set中添加对象时,首先调用此对象所在类的hasCode()方法,计算此对象的hasCode值,此哈希值决定了此对象在set中的存储位置,

若此位置之前没有对象存储,则这个对象直接存到此位置;若此位置已有对象存储,再通过equals方法比较这两个对象是否相同;

如果相同,后一个对象就不能再添加进来;万一返回false,要求hasCode方法要与equals方法一致;

 

hashtable:默认容量是11,继承了Dictionary,实现了Map接口,HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable 的效率非常低下。因为当一个线程访问HashTable的同步方法,其他线程也访问HashTable的同 步方法时,会进入阻塞或轮询状态。get和put方法都进行了加锁,如线程1使用put进行元素添加,线程2不但不能使用put方法添加元素,也不能使用get方法来获取元素,所以竞争越激烈效率越低。不适合高并发。2n+1扩容。

 其他区别:

Hashtable中,key和value都不允许出现null值;2n+1扩容;
HashMap中,null可以作为键,这样的键只有一个,可以有一个或多个键所对应的值为null;2n扩容;

concurrenthashmap:并发度更高,hashtable是一种全表锁,concurrenthashmap分段锁segment1.7版本(一种可重入锁ReentrantLock),1.8之后采用了syn+CAS实现赋值操作,成员变量采用volatile修饰,保证内存可见性,降低锁的粒度,支持并发度更高。 1.8之后给每一个数组的头结点加锁,粒度降低。

ConcurrentHashMap的size方法做法是先尝试2次通过不锁住Segment的方式来统计各个Segment大小,如 果统计的过程中,容器的count发生了变化,则再采用加锁的方式来统计所有Segment的大小(乐观)。ConcurrentHashMap是如何判断在统计的时候容器是否发生了变化呢?使用modCount 变量,在put、remove和clean方法里操作元素前都会将变量modCount进行加1,那么在统计size 前后比较modCount是否发生变化,从而得知容器的大小是否发生变化。

第一次添加元素的时候,默认初期长度为16,当往map中继续添加元素的时候,通过hash值跟table数组长度取与运算来决定要放在数组的哪个位置,如果出现放在同一个位置的时候,优先以链表的形式存放,在同一个位置的个数又达到了8个以上,如果table数组的长度还小于64的时候,则会扩容数组。如果table数组的长度大于等于64了的话,在会将该节点的链表转换成树。

  通过扩容数组的方式来把这些节点给分散开。然后将这些元素复制到扩容后的新的数组中,同一个链表中的元素通过hash值的数组长度位来区分,是还是放在原来的位置还是放到扩容的长度的相同位置去 。在扩容完成之后,如果某个节点的是树,同时现在该节点的个数又小于等于6个了,则会将该树转为链表。

  取元素的时候,相对来说比较简单,通过计算hash来确定该元素在数组的哪个位置,然后在通过遍历链表或树来判断key和key的hash,取出value值。

  这个时候因为数组的长度才为16,则不会转化为树,而是会进行扩容。

  需要注意的是,扩容之后的长度不是32,扩容后的长度在后面细说。

  如果数组扩张后长度达到64了,且继续在某个节点的后面添加元素达到8个以上的时候,则会出现转化为红黑树的情况。

 

get操作不需要加锁,是它的get方法里将要使用的共享变量都定义成volatile类型,如用于统计当前 Segement大小的count字段和用于存储值的HashEntry的value。定义成volatile的变量,能够在线程之间保持可见性,能够被多线程同时读,并且保证不会读到过期的值,但是只能被单线程写 (有一种情况可以被多线程写,就是写入的值不依赖于原值),在get操作里只需要读不需要写 共享变量count和value,所以可以不用加锁。之所以不会读到过期的值,是因为根据Java内存模 型的happen before原则,对volatile字段的写入操作先于读操作,即使两个线程同时修改和获取 volatile变量,get操作也能拿到最新的值,这是用volatile替换锁的经典应用场景。

1.8:1.8的put就很复杂来,会先计算出hash值,然后根据hash值选出Node数组的下标(默认数组是空的,所以一开始put的时候会初始化,指定负载因子是0.75,不可变),判断是否为空,如果为空,则用cas的操作来赋值首节点,如果失败,则因为自旋,会进入非空节点的逻辑,这个时候会用synchronize加锁头节点(保证整条链路锁定)这个时候还会进行二次判断,是否是同一个首节点,在分首节点到底是链表还是树结构,进行遍历判断。

happen-before:jmm(java 内存模型java Memory Model),JMM可以通过happens-before关系向程序 员提供跨线程的内存可见性保证(如果A线程的写操作a与B线程的读操作b之间存在happensbefore关系,尽管a操作和b操作在不同的线程中执行,但JMM向程序员保证a操作将对b操作可见)。

TreeSet:一个有序集合,其底层是基于TreeMap实现的,非线程安全。TreeSet可以确保集合元素处于排序状态。TreeSet支持两种排序方式,自然排序和定制排序,其中自然排序为默认的排序方式。

TreeMap:是Map中的一种很特殊的map,我们知道Map基本是无序的,但是TreeMap是会自动进行排序的,也就是一个有序Map(使用了红黑树来实现),如果设置了Comparator比较器,则会根据比较器来对比两者的大小,如果没有则key需要是Comparable的子类

 

对象:

1.对象头  mark word:存储对象的hasCode,分代年龄和锁标志位消息;

               Klass Point(类型指针)对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

2.实例数据:这部分主要是存放类的数据信息,父类的信息

3.对其填充:由于虚拟机要求对象起始地址必须是8字节的整数倍,填充数据不是必须存在的,仅仅是为了字节对齐。

Tip:不知道大家有没有被问过一个空对象占多少个字节?就是8个字节,是因为对齐填充的关系哈,不到8个字节对其填充会帮我们自动补齐。

 

 CAS:乐观锁技术,当多个线程尝试使用CAS同时更新一个变量时,只有一个线程能成功。(优点:轻量级  缺点:如果忙等一直不成功,会对CPU造成大开销;ABA问题,可以用版本号机制解决原子类)

CAS是英文单词CompareAndSwap的缩写,中文意思是:比较并替换。CAS需要有3个操作数:内存地址V,旧的预期值A,即将要更新的目标值B。

CAS指令执行时,当且仅当内存地址V的值与预期值A相等时,将内存地址V的值修改为B,否则就什么都不做。整个比较并替换的操作是一个原子操作。

 

线程安全:当多个线程访问同一个对象时,如果不用考虑这些线程在运行时环境下的调度和交替运行,也不需要进行额外的同步,或者在调用方进行任何其他的协调操作,调用这个对象的行为都可以获取正确的结果,那这个对象是线程安全的。

 

 

设计模式:

单例模式:

预加载和懒加载:

 

 单例时线程安全的保证:

 

 

 

线程状态

Java中线程状态有6种

public enum State {
        NEW, 新建
        RUNNABLE,运行
        BLOCKED,阻塞
        WAITING,等待
        TIMED_WAITING,睡眠
        TERMINATED;终止
    }

线程间通信:

进程间通讯的方式:

1.管道:单工,固定的读端与写端;速度慢,容量有限,只有父子进程能通讯    

2.FIFO:任何进程间都能通讯,但速度慢    ;可用于非父子进程

3.消息队列:容量受到系统限制,且要注意第一次读的时候,要考虑上一次没有读完数据的问题    

4.信号量:不能传递复杂消息,只能用来同步    

5.共享内存区:能够很容易控制容量,速度快,但要保持同步,比如一个进程在写的时候,另一个进程要注意读写的问题,相当于线程中的线程安全,当然,共享内存区同样可以用作线程间通讯,不过没这个必要,线程间本来就已经共享了同一进程内的一块内存

6.socket:  套接口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同机器间的进程通信。

1.命名socket  2.bind方法绑定        3.监听,事件循环组              4.connect方法连接        5.read和write方法相互发送数据   6.close方法断开连接

通信方法

wait():调用wait方法的线程,当前持有锁的该线程等待,直至该对象的另一个持锁线程调用notify/notifyAll操作

wait(long timeOut)、wait(long timeOut,int nanos)

notify():通知持有该对象锁的所有线程中的的随意一个线程被唤醒

notifyAll():通知持有该对象锁的所有线程被同时唤醒

 

线程通信常用的方式有:

  • wait/notify 等待
  • Volatile 内存共享
  • CountDownLatch 并发工具
  • CyclicBarrier 并发工具

并发是指多个线程操作同一个资源,不是同时执行,而是交替执行并行才是真正的同时执行,多核CPU

start()方法和run()方法的区别:
1,start()方法在Thread类中定义的,子类不需要重写;而run()方法在Runnable接口中定义的,子类需要重写。
2,start()方法一般用来开启线程和调用run()方法;而run()方法是存储线程执行时所要运行的代码的。
wait()和sleep()的区别:
1.wait()方法是因为线程进入等待状态而阻塞的,而sleep()方法是因为线程进入休眠状态而阻塞的。
2.调用wait()方法而阻塞的线程必须调用notify()或notifyAll()方法才能唤醒,而调用sleep()方法阻塞的线程,当睡眠时间结束或调用interrupt()方法,线程都会跳出阻塞状态。
3.wait()必须在同步代码块中使用,而sleep()方法不要求。
4.调用wait()方法进入阻塞时,线程会释放掉锁。而调用sleep()方法进入阻塞时,线程不会释放锁
5.wait()方法可以由任何对象调用,而sleep()方法只能由Thread调用;Thread.sleep(long millins)使线程转到阻塞状态;Object.wait()方法,释放线程锁,使线程进入等待状态;
 

产生死锁的条件?如何避免死锁?

1、因竞争资源产生死锁

2、进程推进顺序不当会发生死锁

死锁产生的四个必要条件

1、互斥条件:一个资源每次只能被一个线程使用

2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

3、不可剥夺条件:进程已获得的资源,在未使用之前,不能强行剥夺

4、循环等待条件:若干进程之间形成一种头尾相连接的循环等待资源关系

 

死锁的解除与预防

解决死锁的三种途径:预防、避免、检测与恢复

死锁预防

预防死锁只需要破坏4个必要条件就可以

资源一次性分配:(破坏请求与保持的)

可剥夺资源:当线程来获取新资源未满足时,需要将已占有资源释放掉(破坏不可剥夺条件)

资源的有序分配法:系统给每一个资源进行编号,每一个线程按照编号递增顺序请求资源,释放资源正好相反(破坏请求与保持)

 

volatile关键字作用(保证共享变量对所有线程的可见性;二是禁止指令重排序优化);原理是使其他线程访问的工作内存失效,而不得不从主存中重新获取。会引起ABA问题,另一个线程对原原本线程操作的栈进行了添加新元素,出栈了本来为栈顶的元素,导致元素游离。解决:版本号机制  AtomicStampedReferce类用stamp属性和referce属性实现CAS时间戳和预期值的更新值对比。

synchronized:是一种同步锁;保证在同一时刻,只有一个线程可以执行某个方法或某个代码块

Java编译器为我们生成的字节码。JVM对于同步方法和同步代码块的处理方式不同。(javap -c 类.class  反汇编命令)

对于同步方法,JVM采用ACC_SYNCHRONIZED标记符来实现同步,JVM检测到有该标志位之后,会

(同步方法的常量池中会有一个ACC_SYNCHRONIZED标志。当某个线程要访问某个方法的时候,会检查是否有ACC_SYNCHRONIZED,如果有设置,则需要先获得监视器锁,然后开始执行方法,方法执行之后再释放监视器锁。这时如果其他线程来请求执行方法,会因为无法获得监视器锁而被阻断住。值得注意的是,如果在方法执行过程中,发生了异常,并且方法内部并没有处理该异常,那么在异常被抛到方法外面之前监视器锁会被自动释放。)

允许使用任何的一个对象作为同步的内容,因此任意一个对象都应该拥有自己的监视器(monitor),当这个对象由同步块或者这个对象的同步方法调用时,执行方法的线程必须先获取到该对象的监视器才能进入同步块或者同步方法,而没有获取到监视器(执行该方法)的线程将会被阻塞在同步块和同步方法的入口处,进入BLOCKED状态。监视器锁本质又是依赖于底层的操作系统的Mutex Lock来实现的。而操作系统实现线程之间的切换就需要从用户态转换到核心态,这个成本非常高,状态之间的转换需要相对比较长的时间,这就是为什么Synchronized效率低的原因。因此,这种依赖于操作系统Mutex Lock所实现的锁我们称之为“重量级锁”。优化:锁升级机制

锁升级过程:开始是无锁状态,锁对象第一次被线程获取时,虚拟机将会把对象头中的标志位设位01,即偏向模式,同时使用CAS操作把获取到这个锁的线程线程的ID记录在对象的Mark Word之中。但当有另外的线程去尝试获取这个锁时,就需要查看锁对象头中记录的那个线程是否还存活,如果没有存活,那么锁对象就会被置为无锁状态,且这时候其他线程是可以竞争该锁,如果获取成功该锁,该锁就又被设为偏向锁;如果对象头中记录的那个线程仍存活,那就立即查找该线程的栈帧信息,判断是否还需要此锁,如果不需要,那么该锁对象就会被置为无锁状态,且偏向其他新的线程,如果还需要此锁,那么就先暂停当前线程,撤销掉偏向锁,升级为轻量级锁(00)的状态。多个线程同一时间竞争同一个线程,会进行自旋,自旋到一定次数(默认10)会升级成重量级锁,保证了性能。

同步代码块,JVM采用monitorentermonitorexit两个指令来实现同步。(可以把执行monitorenter指令理解为加锁,执行monitorexit理解为释放锁。 每个对象维护着一个记录着被锁次数的计数器。未被锁定的对象的该计数器为0,当一个线程获得锁(执行monitorenter)后,该计数器自增变为 1 ,当同一个线程再次获得该对象的锁的时候,计数器再次自增。当同一个线程释放锁(执行monitorexit指令)的时候,计数器再自减。当计数器为0的时候。锁将被释放,其他线程便可以获得锁。monitor监视器源码是C++写的)

 

为什么不用syn?

一方面是因为synchronized是一种锁机制,存在阻塞问题和性能问题,而volatile并不是锁,所以不存在阻塞和性能问题。

另外一方面,因为volatile借助了内存屏障来帮助其解决可见性和有序性问题,而内存屏障的使用还为其带来了一个禁止指令重排的附件功能,所以在有些场景中是可以避免发生指令重排的问题的。

 

 

 

syn和ReentrantLock:

用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!!

Lock和synchronized有以下几点不同:

  1)Lock是一个接口,而synchronized是Java中的关键字,synchronized是内置的语言实现;

  2)synchronized在发生异常时,会自动释放线程占有的锁,因此不会导致死锁现象发生;而Lock在发生异常时,如果没有主动通过unLock()去释放锁,则很可能造成死锁现象,因此使用Lock时需要在finally块中释放锁;

  3)Lock可以让等待锁的线程响应中断(ReentrantLock给我们提供了一个可以响应中断的获取锁的方lockInterruptibly()),使用synchronized时,等待的线程会一直等待下去,不能够响应中断;

  4)通过Lock可以知道有没有成功获取锁,而synchronized却无法办到。

  5)Lock可以提高多个线程进行读操作的效率。

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。Lock可以使用读锁提高多线程读效率。场景是一定要考虑的,我现在告诉你哪个好都是扯淡,因为脱离了业务,一切技术讨论都没有了价值。

整体上来说 Lock 是 synchronized 的扩展版,Lock 提供了无条件的、可轮询的(tryLock 方法)、定时的(tryLock 带参方法)、可中断的(lockInterruptibly)、可多条件队列的(newCondition 方法)锁操作。另外 Lock 的实现类基本都支持非公平锁(默认)和公平锁,synchronized 只支持非公平锁,当然,在大部分情况下,非公平锁是高效的选择。

 

ReentrantLock,意思是“可重入锁”。ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法;syn也可重入; 是如何实现可重入性的 ?内部自定义了同步器 Sync,加锁的时候通过CAS 算法(CAS设置state) ,将线程对象放到一个双向链表 中,每次获取锁的时候 ,看下当前维 护的那个线程ID和当前请求的线程ID是否一样,一样就可重入了;原理:CAS+AQS队列来实现
(1):先通过CAS尝试获取锁, 如果此时已经有线程占据了锁,那就加入AQS队列并且被挂起;
(2): 当锁被释放之后, 排在队首的线程会被唤醒CAS再次尝试获取锁,
(3):如果是非公平锁, 同时还有另一个线程进来尝试获取可能会让这个线程抢到锁;
(4):如果是公平锁, 会排到队尾,由队首的线程获取到锁。

ReentrantLock解决死锁:线程通过调用tryLock()方法获取锁,第一次获取锁失败时会休眠10毫秒,然后重新获取,直到获取成功。第二次获取失败时,首先会释放第一把锁,再休眠10毫秒,然后重试直到成功为止。线程获取第二把锁失败时将会释放第一把锁,这是解决死锁问题的关键,避免了两个线程分别持有一把锁然后相互请求另一把锁。可以对获取锁的等待时间进行设置,避免死锁。

多线程并发编程中主要围绕着三个特性实现:可见性、原子性、有序性

指令重排序 :是编译器和处理器为了优化程序执行的性能而对指令序列进行重排的一种手段。现象就是CPU 执行指令的顺序可能和程序代码的顺序不一致,不会对存在依赖关系的操作进行重排

谈谈volatile和synchronized两者的区别:

  volatile:可见性。禁止重排序。

  synchronized:原子性,可见性,可重排序,会阻塞。

  从使用上来看

  1) volatile关键字是变量修饰符,只能用于修饰实例变量或者类变量,不能用于修饰方法以及方法参数、局部变量、常量等;

  2) synchronized关键字不能用于对变量的修饰,只能用于修饰方法或者语句块或类

  3) volatile修饰的变量可以为null,synchronized关键字同步语句块的monitor对象不能为null;

  对原子性的保证

  1) volatile无法保证原子性;(因此volatile无法替代synchronized)

  2) 由于synchronized是一种排他的机制,因此被synchronized关键字修饰的同步代码是无法被中途打断的,因此其能够保证代码的原子性;

  对可见性的保证

    两者均可以保证共享资源在多线程间的可见性,但是实现机制完全不同

  1) Synchronized借助于JVM指令monitor enter和monitor exit对通过排他的方式使得同步代码串行化,在monitor exit时所有共享资源都将会被刷新到主内存中;(因为每次只允许一个线程进行操作)

  2) 相比于synchronized关键字,volatile使用机器指令“lock;”的方式迫使其他线程工作内存中的数据失效,不 得不从主内存中进行再次加载;

  对有序性的保证

  1) volatile关键字禁止JVM编译器以及处理器对其进行重排序,所有它能够保证有序性;

  2) 虽然synchronized关键字所修饰的同步方法也可以保证顺序性,但是这种顺序性是以程序的串行化执行换来的,在synchronized关键字所修饰的代码块中代码指令也会发生指令重排序的情况,但是由于synchronized关键字同步的作用,所以对程序来说没有任何的影响;

  其他

  1) volatile不需要加锁,比synchronized更轻量级,而且不会使得线程陷入阻塞,synchronized关键字会使得线程进入阻塞状态;

  2) volatile标记的变量不会被编译器优化,而synchronized标记的变量可以被编译器优化(如编译器重排序的优化)

AQS:Node内部类构成的一个双向链表结构的同步队列,通过控制(volatile的int类型)state状态来判断锁的状态,对于非可重入锁状态不是0则去阻塞;对于可重入锁如果是0则执行,非0则判断当前线程是否是获取到这个锁的线程,是的话把state状态+1,比如重入5次,那么state=5。 而在释放锁的时候,同样需要释放5次直到state=0其他线程才有资格获得锁;两种资源共享方式:Exclusive:独占,只有一个线程能执行,如ReentrantLock;Share:共享,多个线程可以同时执行,如Semaphore、CountDownLatch、ReadWriteLock,CyclicBarrier

 

 CountDownLatch和CyclicBarrier区别

  • con用于主线程等待其他子线程任务都执行完毕后再执行,cyc用于一组线程相互等待大家都达到某个状态后,再同时执行;
  • CountDownLatch是不可重用的,CyclicBarrier可重用
  • CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务

 

 线程池种类:

 - newFixedThreadPool可以生成固定大小的线程池;阻塞队列为无界队列LinkedBlockingQueue;适用于处理CPU密集型的任务,适用执行长期的任务

 - newCachedThreadPool可以生成一个无界、可以自动回收的线程池;阻塞队列是SynchronousQueue;适用于并发执行大量短期的小任务

 - newSingleThreadScheduledExecutor可以生成一个单个线程的线程池;阻塞队列是LinkedBlockingQueue;适用于串行执行任务的场景,一个任务一个任务地执行

 - newScheduledThreadPool还可以生成支持周期任务的线程池。阻塞队列是DelayedWorkQueue;周期性执行任务的场景,需要限制线程数量的场景

 

有两种方式提交任务:
1.使用void execute(Runnable command)方法提交任务
execute方法返回类型为void,所以没有办法判断任务是否被线程池执行成功。
2.使用submit方法提交任务
可以通过这个future来判断任务是否执行成功,通过future的get方法来获取返回值,get方法会阻塞直到任务完成,而使用get(long timeout, TimeUnit unit)方法则会阻塞一段时间后立即返回,这时有可能任务没有执行完。

 

线程池任务提交执行的步骤:

1.提交一个任务,线程池里存活的核心线程数小于corePoolSize时,线程池会创建一个核心线程去处理提交的任务
2.如果线程池核心线程数已满,即线程数已经等于corePoolSize,一个新提交的任务,会被放进任务队列workQueue排队等待执行。
3.当线程池里面存活的线程数已经等于corePoolSize了,并且任务队列workQueue也满,判断线程数是否达到maximumPoolSize,即4.最大线程数是否已满,如果没到达,创建非核心线程执行提交的任务。
5.如果当前的线程数达到了maxiPoolSize,还有新的任务过来的话,直接采用拒绝策略处理。

任务拒绝策略

当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常有以下四种策略

    ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常。 (默认)

    ThreadPoolExecutor.DiscardPolicy:直接丢弃任务,不抛出异常。

    ThreadPoolExecutor.DiscardOldestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    ThreadPoolExecutor.CallerRunsPolicy:由调用线程处理该任务;如果被丢弃的线程任务未关闭,则执行该线程
 
//      创建线程的第一种方法
        Thread1 thread1 = new Thread1();
        thread1.start();
 
//      创建线程的第二种方法
        Thread2 thread2 = new Thread2();
        Thread thread = new Thread(thread2);
        thread.start();
 
//      创建线程的第三种方法
        Callable<String> callable = new Thread3();
        FutureTask<String> futureTask = new FutureTask<>(callable);
        Thread thread3 = new Thread(futureTask);
        thread3.start();
        String s = futureTask.get();
        System.out.println(s);
 
//      创建线程的第四种方法
        Executor executor = Executors.newFixedThreadPool(5);
        executor.execute(new Runnable() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread()+"创建线程的第四种方法");
            }
        });
        ((ExecutorService) executor).shutdown();

 

高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池?

这是我在并发编程网上看到的一个问题,把这个问题放在最后一个,希望每个人都能看到并且思考一下,因为这个问题非常好、非常实际、非常专业。关于这个问题,个人看法是:

(1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换

(2)并发不高、任务执行时间长的业务要区分开看:

  a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务

  b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换

(3)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考(2)最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。

悲观锁总是假设最坏的情况,每次去拿数据的时候都认为别人会修改,所以每次在拿数据的时候都会上锁,这样别人想拿这个数据就会阻塞直到它拿到锁(共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程)。传统的关系型数据库里边就用到了很多这种锁机制,比如行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁。Java中synchronized和ReentrantLock等独占锁就是悲观锁思想的实现.。

乐观锁总是假设最好的情况,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号机制和CAS算法实现。乐观锁适用于多读的应用类型,这样可以提高吞吐量,其实都是提供的乐观锁。在Java中java.util.concurrent.atomic包下面的原子变量类就是使用了乐观锁的一种实现方式CAS实现的。

 

vector和ArrayList和linkedList的区别:
ArrayList实现是一个动态数组,可变数组,默认初始化长度为10,也可以我们设置容量,但是没有设置的时候是默认的空数组,只有在第一步add的时候会进行扩容至10(重新创建了数组),后续扩容按照3/2的大小进行扩容,是线程不安全的,适用多读取,少插入的情况
linkedList是基于双向链表的实现,使用了尾插法的方式,内部维护了链表的长度,以及头节点和尾节点,所以获取长度不需要遍历。适合一些插入/删除频繁的情况。
Vector是线程安全的,实现方式和ArrayList相似,也是基于数组,2倍扩容,但是方法上面都有synchronized关键词修饰。其扩容方式是原来的两倍。

 

Array 和 ArrayList 有何区别?

Array可以容纳基本类型和对象,而ArrayList只能容纳对象。
Array是指定大小的,而ArrayList大小是固定的。
Array没有提供ArrayList那么多功能,比如addAll、removeAll和iterator等。

数组链表区别:

 

 

 

 

 

在 Queue 中 poll()和 remove()有什么区别?

poll() 和 remove() 都是从队列中取出一个元素,但是 poll() 在获取元素失败的时候会返回null,但是 remove() 失败的时候会抛出异常。

 

5.java 8新特性? lambada表达式 :java也开始承认了函数式编程, 就是说函数既可以作为参数,也可以作为返回值;

Lambda表达式的好处?
        1.函数式编程是技术的发展方向,而Lambda时函数式编程最基础的内容,所以,Java8中加入Lambda表达式本身时符合技术发展方向的。
        2.通过引入Lambda,最直观的一个改进是不用再写大量的匿名内部类,还有更多由于函数式编程本身特性带来的提升。比如:代码的可读性会更好,高阶函数引入了函数组合的概念。
        3.Lambda的引入,集合操作也得到了极大的改善。

stream流:

default关键字:打破接口里面是只能有抽象方法,不能有任何方法的实现,接口里面也可以有方法的实现了

6.业务与技术的关系技术服务于业务,业务驱动技术。良性循环

7.jvm内存模型?什么情况下会full gc?垃圾回收机制?类加载过程?oom?

如何查看JVM的内存使用情况

jps:JVM Process Status Tool,显示指定系统内所有的HotSpot虚拟机进程。

jstat:(JVM statistics Monitoring)是用于监视虚拟机运行时状态信息的命令,它可以显示出虚拟机进程中的类装载、内存、垃圾收集、JIT编译(Just In Time Compiler, 即时编译器)等运行数据。

 

 

java内存模型:Java内存模型将内存分为了主内存和工作内存

Java内存模型规定了所有的变量都存储在主内存中,每条线程还有自己的工作内存,线程的工作内存中保存了该线程中是用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存。不同的线程之间也无法直接访问对方工作内存中的变量,线程间变量的传递均需要自己的工作内存和主存之间进行数据同步进行。

而JMM就作用于工作内存和主存之间数据同步过程。他规定了如何做数据同步以及什么时候做数据同步。

JAVA

As-if-serial:所有的动作(Action)都可以为了优化而被重排序,但是必须保证它们重排序后的结果和程序代码本身的应有结果是一致的。Java编译器、运行时和处理器都会保证单线程下的as-if-serial语义。

jvm内存结构:堆和方法区共享,其他私有;程序计数器不会OOM

 

 

本地方法栈(native方法在运行过程中需要的运行空间)、(存放实例化的对象)、方法区(常量、静态变量(static)、类信息、包括运行时常量池)、

程序计数器(PC寄存器,存储本线程下一条指令的地址,保证程序的顺序执行。线程私有)、

虚拟机栈(每个Java方法的执行对应着一个栈帧的进栈和出栈的操作,它保存方法的局部变量(8种基本数据类型、对象的引用地址)、部分结果,并参与方法的调用和返回);

 

每个栈帧中存储着:

  • 局部变量表(Local Variables)
  • 操作数栈(Operand Stack)(或表达式栈)
  • 动态链接(Dynamic Linking)(或执行运行时常量池的方法引用)
  • 方法返回地址(Return Adress)(或方法正常退出或者异常退出的定义)
  • 一些附加信息

虚拟机栈与本地方法栈区别是虚拟机栈为虚拟机执行java方法服务,本地方法栈则为虚拟机使用到的native方法服务。

线程私有:程序计数器,虚拟机栈,本地方法栈

Java中的数组是存储在堆上还是栈上的?

在Java中,数组同样是一个对象,所以对象在内存中如何存放同样适用于数组;

所以,数组的实例是保存在堆中,而数组的引用是保存在栈上的。

 HotSpot JVM把年轻代分为了三部分:1个Eden区和2个Survivor区(分别叫from和to)。默认比例为8:1:1。一般情况下,新创建的对象都会被分配到Eden区(一些大对象特殊处理),这些对象经过第一次Minor GC后,如果仍然存活,将会被移到Survivor区。对象在Survivor区中每熬过一次Minor GC,年龄就会增加1岁,当它的年龄增加到一定程度时(默认15),就会被移动到年老代中。

  因为年轻代中的对象基本都是朝生夕死的(80%以上),所以在年轻代的垃圾回收算法使用的是复制算法,复制算法的基本思想就是将内存分为两块,每次只用其中一块,当这一块内存用完,就将还活着的对象复制到另外一块上面。复制算法不会产生内存碎片。在GC开始的时候,对象只会存在于Eden区和名为“From”的Survivor区,Survivor区“To”是空的。紧接着进行GC,Eden区中所有存活的对象都会被复制到“To”,而在“From”区中,仍存活的对象会根据他们的年龄值来决定去向。年龄达到一定值(年龄阈值,可以通过-XX:MaxTenuringThreshold来设置)的对象会被移动到年老代中,没有达到阈值的对象会被复制到“To”区域。经过这次GC后,Eden区和From区已经被清空。这个时候,“From”和“To”会交换他们的角色,也就是新的“To”就是上次GC前的“From”,新的“From”就是上次GC前的“To”。不管怎样,都会保证名为To的Survivor区域是空的。Minor GC会一直重复这样的过程,直到“To”区被填满,“To”区被填满之后,会将所有对象移动到年老代中。

 

共享:堆、方法区

full gc: 1.年老代(Tenured)被写满

2.持久代(Perm)被写满

3.System.gc()被显示调用

4.上一次GC之后Heap的各域分配策略动态变化

hotSpot虚拟机(1.8后)移除了永久代,使用本地内存来存储类元数据信息并称之为:元空间(Metaspace)

判断对象是否存活的算法:引用计数法(给对象添加计数器,被引用+1,引用失效-1,为0表示不可能再被引用)  

根可达算法(“GC Roots”的对象作为起始点,从这些节点开始向下搜索,搜索所走过的路径称为引用链(Reference Chain),当一个对象GC Roots没有任何引用链相连(用图论的话来说就是从GC Roots到这个对象不可达)时,则证明此对象是不可用的。)

 

垃圾回收机制:算法:标记-清除算法(产生碎片)、标记-整理算法(整理碎片)、复制算法(划分内存,清除垃圾对象)、分代算法

分代:

在新生代中(Eden、From Survivor、To Survivor区),每次垃圾收集时都发现有大批对象死去,只有少量存活,那就选用复制算法,只需要付出少量存活对象的复制成本就可以完成收集。而老年代中因为对象存活率高、没有额外空间对它进行分配担保,就必须使用:“标记-清理”或“标记-整理”算法来进行回收。

 

在Java语言里,可作为GC Roots的对象包括下面几种:

1:系统类加载器加载的对象

2:处于激活状态的线程

3:JNI栈中的对象

4:正在被用于同步的各种锁对象

5:JVM自身持有的对象,比如系统类加载器等。

 

强引用、软引用、弱引用、虚引用:

1.强引用

强引用(Strong Reference)是指JVM内存管理器从根引用集合(Root Set)出发遍寻堆中所有到达对象的路径。当到达某对象的任意路径都不含有引用对象时,对这个对象的引用就被称为强引用。

2.软引用

软引用(Soft Reference)的主要特点是具有较强的引用功能。只有当内存不够的时候,才回收这类内存,因此在内存足够的时候,它们通常不被回收。另外,这些引用对象还能保证在Java抛出OutOfMemory 异常之前,被设置为null。它可以用于实现一些常用资源的缓存,实现Cache的功能,保证最大限度的使用内存而不引起OutOfMemory。

3.弱引用

弱引用(Weak Reference)对象与Soft引用对象的最大不同就在于:GC在进行回收时,需要通过算法检查是否回收Soft引用对象,而对于Weak引用对象, GC总是直接进行回收。因此Weak引用对象会更容易、更快被GC回收。

4.虚引用

虚引用(Phantom Reference)的用途较少,主要用于辅助finalize函数的使用。Phantom对象指一些执行完了finalize函数,并且为不可达对象,但是还没有被GC回收的对象。这种对象可以辅助finalize进行一些后期的回收工作,我们通过覆盖Reference的clear()方法,增强资源回收机制的灵活性。

 

 

1、内存溢出:你申请了10个字节的空间,但是你在这个空间写入11或以上字节的数据,出现溢出。OutOfMemoryError错误
2、内存泄漏:你用new申请了一块内存,后来很长时间都不再使用了(按理应该释放),但是因为一直被某个或某些实例所持有导致 GC 不能回收,也就是该被释放的对象没有释放。

Memory Leak;  1)首先,这些对象是可达的,即在有向图中,存在通路可以与其相连;
2)其次,这些对象是无用的,即程序以后不会再使用这些对象。
如果对象满足这两个条件,这些对象就可以判定为Java中的内存泄漏,这些对象不会被GC所回收,然而它却占用内存。

内存泄漏情况:无法释放已经申请的。

1.各种连接,数据库连接,网络连接,IO连接等没有显示调用close关闭,不被GC回收导致内存泄露

2.监听器的使用,在释放对象的同时没有相应删除监听器的时候也可能导致内存泄露

3.对象相互引用  v.add(new Object())  new Object=null; o已经被置空,但是v依然指向o的空间。

Minor GC:清理新生代

Major GC:清理老年代

Full GC:清理整个堆空间(包括新生代和老年代)

 内存溢出 out of memory,是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。分配的超过了系统能给的。

引起内存溢出的原因有很多种,常见的有以下几种:

1.内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2.集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3.代码中存在死循环或循环产生过多重复的对象实体;
4.启动参数内存值设定的过小

垃圾回收器:

(1): Serial 收集器 复制算法,单线程,新生代)
(2): ParNew 收集器(复制算法,多线程,新生代)
(3): Parallel Scavenge 收集器(多线程,复制算法,新生代,高吞吐量)
(4):Serial Old 收集器(标记-整理算法,老年代)
(5):Parallel Old 收集器(标记-整理算法,老年代,注重吞吐量的场景下,jdk8默认采用 Parallel Scavenge + Parallel Old 的组合)

CMS 收集器:CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器。从名称是上看出 CMS 采用的是标记清除算法。

CMS收集器主要优点:并发收集(GC可以与用户线程),低停顿。

步骤:

1)初始标记
2)并发标记
3)重新标记
4)并发清除
  初始标记、从新标记这两个步骤仍然需要“stop the world”,初始标记仅仅只是标记一下GC Roots能直接关联到的对象,熟读很快,并发标记阶段就是进行GC Roots Tracing,而重新标记阶段则是为了修正并发标记期间因用户程序继续运作而导致标记产生表动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段稍长点,但远比并发标记的时间短。

G1(Garbage-First)收集器的优势:

(1)并行与并发

(2)分代收集

(3)空间整理 (将整个Java堆划分为多个大小相等的独立区域(Region),标记整理算法,复制算法)

(4)可预测的停顿

G1中提供了三种模式垃圾回收模式
(1):young gc(eden region被耗尽无法申请内存时,就会触发)
(2):mixed gc(当老年代大小占整个堆大小百分比达到该阈值时,会触发)
(3):full gc(对象内存分配速度过快,mixed gc来不及回收,导致老年代被填满,就会触发)

CMS和G1区别?

CMS基于标记清除,清理老年代;G1标记整理和复制,都会清理;可预测的停顿:这是G1相对于CMS的另一大优势,降低停顿时间是G1和CMS共同的关注点,但G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用这明确指定一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。

jdk1.8 默认垃圾收集器Parallel Scavenge(新生代)+Parallel Old(老年代)
jdk1.9 默认垃圾收集器G1

jvm类加载过程:
加载(将字节码文件加载进内存,创建.Class对象)链接(确保被加载的类满足虚拟机规范并为静态成员分配空间设置默认初始值)初始化(对静态变量或语句块进行初始化,如果该类有父类,先初始化其父类)

 

OOM:当JVM因为没有足够的内存来为对象分配空间并且垃圾回收器也已经没有空间可回收时,就会抛出这个error(不是异常);

OOM原因:1)分配的少了:比如虚拟机本身可使用的内存(一般通过启动时的VM参数指定)太少。
 2)应用用的太多,并且用完没释放,浪费了。此时就会造成内存泄露或者内存溢出。
 
 
StackOverFlow:线程请求的栈深度大于虚拟机深度(递归没有写结束条件);检查对数据库查询中,是否有一次获得全部数据的查询;

8.网络:
tcp网络编程的过程、网络层传输层区别;比如登录功能,从用户到服务器整个流程

BIO、AIO、NIO区别? Netty?socket?  IO多路复用?

三次握手?四次挥手?拥塞控制?tcp udp区别?

第三次握手,还要发送一次确认是为了防止已失效的连接请求报文段突然又传到了B。
假设只有两次连接的情况下:A发出请求连接,B做出回应,连接正常建立。但是如果A发送一个报文段后,节点可能会产生滞留情况从而导致延误到达,当到达后,B会发出回应;
但A知道自己并没有发送连接的请求,所以不会理睬B的这个确认。这时,A也不会发送任何数据,而B却以为新的连接建立起来了,一直在等待A给自己发送消息,这就会导致B的资源会被白白浪费
而如果采用了第三次握手,A知道自己发送失误后,就不会发送给B的第二次确认,那么B也不会收到A的确认,也就知道连接并没有成功建立!
四次挥手?
由于TCP存在半关闭的状态(单方向的关闭)造成的。因为TCP的连接时全双工的,所以在关闭时需要在每个方向上单独就行关闭。当一方完成它的数据发送任务,就发送一个FIN来告知另一方要终止这个方向的连接。

三次握手:服务器第一次收到客户端的 SYN 之后,就会处于 SYN_RCVD 状态,此时双方还没有完全建立其连接,服务器会把此种状态下请求连接放在一个队列里,我们把这种队列称之为半连接队列
当然还有一个全连接队列,就是已经完成三次握手,建立起连接的就会放在全连接队列中。如果队列满了就有可能会出现丢包现象。
这里在补充一点关于SYN-ACK 重传次数的问题:
服务器发送完SYN-ACK包,如果未收到客户确认包,服务器进行首次重传,等待一段时间仍未收到客户确认包,进行第二次重传。如果重传次数超过系统规定的最大重传次数,系统将该连接信息从半连接队列中删除。
注意,每次重传等待的时间不一定相同,一般会是指数增长,例如间隔时间为 1s,2s,4s,8s…

其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。

SYN攻击是什么?
服务器端的资源分配是在二次握手时分配的,而客户端的资源是在完成三次握手时分配的,所以服务器容易受到SYN洪泛攻击。SYN攻击就是Client在短时间内伪造大量不存在的IP地址,并向Server不断地发送SYN包,Server则回复确认包,并等待Client确认,由于源地址不存在,因此Server需要不断重发直至超时,这些伪造的SYN包将长时间占用未连接队列,导致正常的SYN请求因为队列满而被丢弃,从而引起网络拥塞甚至系统瘫痪。SYN 攻击是一种典型的 DoS/DDoS 攻击。

 

【问题1】为什么连接的时候是三次握手,关闭的时候却是四次握手?
答:因为当Server端收到Client端的SYN连接请求报文后,可以直接发送SYN+ACK报文。其中ACK报文是用来应答的,SYN报文是用来同步的。但是关闭连接时,当Server端收到FIN报文时,很可能并不会立即关闭SOCKET,所以只能先回复一个ACK报文,告诉Client端,"你发的FIN报文我收到了"。只有等到我Server端所有的报文都发送完了,我才能发送FIN报文,因此不能一起发送。故需要四步握手。
【问题2】为什么TIME_WAIT状态需要经过2MSL(最大报文段生存时间)才能返回到CLOSE状态?
答:虽然按道理,四个报文都发送完毕,我们可以直接进入CLOSE状态了,但是我们必须假象网络是不可靠的,有可以最后一个ACK丢失。所以TIME_WAIT状态就是用来重发可能丢失的ACK报文。在Client发送出最后的ACK回复,但该ACK可能丢失。Server如果没有收到ACK,将不断重复发送FIN片段(如果这是客户端已经closed,那么就会响应rst而不是fin)。所以Client不能立即关闭,它必须确认Server接收到了该ACK。Client会在发送出ACK之后进入到TIME_WAIT状态。Client会设置一个计时器,等待2MSL的时间。如果在该时间内再次收到FIN,那么Client会重发ACK并再次等待2MSL。所谓的2MSL是两倍的MSL(Maximum Segment Lifetime)。MSL指一个片段在网络中最大的存活时间,2MSL就是一个发送和一个回复所需的最大时间。如果直到2MSL,Client都没有再次收到FIN,那么Client推断ACK已经被成功接收,则结束TCP连接。
【问题3】为什么不能用两次握手进行连接?
答:3次握手完成两个重要的功能,既要双方做好发送数据的准备工作(双方都知道彼此已准备好),也要允许双方就初始序列号进行协商,这个序列号在握手过程中被发送和确认。
       现在把三次握手改成仅需要两次握手,死锁是可能发生的。作为例子,考虑计算机S和C之间的通信,假定C给S发送一个连接请求分组,S收到了这个分组,并发 送了确认应答分组。按照两次握手的协定,S认为连接已经成功地建立了,可以开始发送数据分组。可是,C在S的应答分组在传输中被丢失的情况下,将不知道S 是否已准备好,不知道S建立什么样的序列号,C甚至怀疑S是否收到自己的连接请求分组。在这种情况下,C认为连接还未建立成功,将忽略S发来的任何数据分 组,只等待连接确认应答分组。而S在发出的分组超时后,重复发送同样的分组。这样就形成了死锁。
【问题4】如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
 
 TIME_WAIT表示主动关闭(第四次挥手完之后客户端保持的状态),    CLOSE_WAIT表示被动关闭等待(第二次挥手完之后服务端保持的状态);FIN_WAIT终止等待(客户端前两次挥手处的状态,分别1和2)
拥塞控制:TCP的四种拥塞控制算法  1.慢开始  2.拥塞控制  3.快重传  4.快恢复
对网络中某一资源的需求超过了该资源所能提供的可用部分,网络性能就要变坏,这种情况就叫做网络拥塞
慢开始:建立连接后,拥塞窗口cwnd(发送窗口swnd)的值被设置为1,还需设置慢开始门限ssthresh,发送方每收到一个对新报文段的确认时,就把拥塞窗口cwnd的值加一,然后开始下一轮的传输,当拥塞窗口cwnd增长(指数增长)到慢开始门限值时,就使用拥塞避免算法。
拥塞控制:拥塞窗口值增长到慢开始门限后,实行拥塞避免,增长到拥塞窗口后,假如有失效的报文段,重传失效后,发送方会判断可能出现了拥塞,更改cwnd和ssthresh(cwnd减少为1,ssh减少为原来cwnd的一半).并重新开始慢开始算法;拥塞控制是指将cwnd控制为线性规律增长;
快重传:不用等接收方确认可以继续发送;一旦收到3个连续的报文确认,就应立即重传,而不是等超时重传计数器超时之后再重传。意识到丢失报文段之后,执行快恢复算法。
快恢复:发送端将ssh和cwnd调整为ssh初值的一半,继续实行拥塞避免。
 

TCP短连接: Client向Server发起连接请求,Server接到请求,然后双方建立连接。Client向Server发送消息,Sever回应Client,然后一次读写就完成了,这时候双方任何一个都可以发起Close操作,不过一般都是Client先发起Close操作。因为一般的Server不会回复完Client后就立即关闭连接,当然也不排除特殊情况。 短连接一般用于一点对多点的通信。WEB网站的HTTP服务一般都采用短连接方式。

连接的优点: 管理起来比较简单,存在的连接都是有用的连接,节省服务器的资源,不需要额外的控制手段。

TCP长连接  :长连接常用于点对点P2P的通信。

在TCP层握手成功后,不立即断开连接,并在此连接的基础上进行多次消息(包括心跳)交互,直至连接的任意一方(客户端OR服务端)主动断开连接,此过程称为一次完整的长连接。HTTP 1.1相对于1.0最重要的新特性就是引入了长连接。

TCP短连接与长连接的区别:客户端收到服务端的响应后,立刻发送FIN消息,主动释放连接。也有服务端主动断连的情况,凡是在一次消息交互(发请求-收响应)之后立刻断开连接的情况都称为短连接。

注:短连接是建立在TCP协议上的,有完整的握手挥手流程,区别于UDP协议。

 

TCP和UDP的区别

 IP是 Internet protocol 的缩写,是网络层的主要协议,作用是提供不可靠、无连接的数据包传输协传送。

TCP(传输控制协议):面向连接(发送连接之前需要建立三次握手)、可靠的(向上层提供)、流式服务、首部开销(源、目的、序号、确认号、检验和、紧急指针)20个字节、每一条TCP连接只能是点到点的、

UDP(用户数据报协议):无连接,不可靠的(向上层提供)、数据包服务、首部开销(源端口、目的端口、长度、检验和)8个字节、UDP支持一对一,一对多,多对一和多对多的交互通信

应用层(数据) 

传输层(分段,TCP报文段):保证源程序到目的程序的端到端的可靠通信(端口)   端到端    

     三次握手、四次挥手                         TCP、UDP协议

网络层(分组:IP数据报):源主机到目的主机的可靠通信(IP)                           点到点  

       路由选择、网络寻址、流量控制       IP协议、ICMP Internet控制报文协议

数据链路层(帧):通过各种控制协议,将有差错的物理信道变为无差错的、能可靠传输数据帧的数据链路。

物理层(比特流):利用传输介质为数据链路层提供物理连接,实现比特流的透明传输。

 

TCP如何保证数据可靠性

1、校验和(16位):报文头的检验和用来保证当前传输包的完整性

2、序列号seq:报文头中的序列号是用来对TCP包进行编号,接收端通过编号可以对数据进行去重和排序,对发送的数据进行编号,接收方返回的确认序号就是下一个发送包的编号

3、确认应答机制(ACK 属于标志位)

4、拥塞控制 超时重传/快重传

5、流量控制:滑动窗口、停等协议(发送窗口和接收窗口都等于1时)

 

 

AIO、NIO、BIO

1.BIO:同步阻塞,采用BIO通信模型的服务端,通常由一个独立的Acceptor线程负责监听客户端的连接,它接收到客户端连接请求之后为每个客户端创建一个新的线程进行链路处理,处理完成后,通过输出流返回应答给客户端,线程销毁。即典型的一请求一应答通信模型。

2.NIO:在JDK1.4以前,Java的IO模型一直是BIO,但从JDK1.4开始,JDK引入的新的IO模型NIO,它是同步非阻塞的。客户端和服务器之间通过Channel通信。NIO可以在Channel中进行读写操作,但使用时要与Buffer结合。如果客户有请求时,服务器会为每个Client分配一个Channel,这些Channel都会被注册在Selector多路复用器上。如果某个Channel中没有事件,线程不会一直阻塞到Channel中,此时Selector通过一个线程不停的轮询这些Channel,找出已经准备就绪的Channel执行IO操作,如果注册到Selector上的每一个Channel都没有事件要读取,则线程还可以去做其他的业务。所以说NIO 是通过一个线程轮询,实现千万个客户端的请求,这也就是非阻塞NIO的特点。

3.AIO:其核心思想是:异步非阻塞IO。内核先把数据准备好,然后再把数据主动拷贝到程序空间,准备数据和拷贝数据程序都不参与,拷贝好了,系统通知程序可以执行程序了,但AIO现在没有真正的实例,所以一般谈得比较少。

 

Java NIO: Channels and Buffers(通道和缓冲区)

标准的IO基于字节流和字符流进行操作的,而NIO是基于通道(Channel)和缓冲区(Buffer)进行操作,数据总是从通道读取到缓冲区中,或者从缓冲区写入到通道中。

Java NIO: Non-blocking IO(非阻塞IO)

Java NIO可以让你非阻塞的使用IO,例如:当线程从通道读取数据到缓冲区时,线程还是可以进行其他事情。当数据被写入到缓冲区时,线程可以继续处理它。从缓冲区写入通道也类似。

Java NIO: Selectors(选择器)

Java NIO引入了选择器的概念,选择器用于监听多个通道的事件(比如:连接打开,数据到达)。因此,单个的线程可以监听多个数据通道。

仅用单个线程来处理多个Channels的好处是,只需要更少的线程来处理通道。事实上,可以只用一个线程处理所有的通道。对于操作系统来说,线程之间上下文切换的开销很大,而且每个线程都要占用系统的一些资源(如内存)。因此,使用的线程越少越好。

SocketChannel 能通过TCP读写网络中的数据。

ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

 

Socket的概念很简单,它是网络上运行的两个程序间双向通讯的一端,既可以接收请求,也可以发送请求,利用它可以较为方便地编写网络上数据的传递

所以简而言之,Socket就是进程通信的端点,Socket之间的连接过程可以分为几步:

1、服务器监听

服务器端Socket并不定位具体的客户端Socket,而是处于等待连接的状态,实时监控网络状态

2、客户端请求

客户端Socket发出连接请求,要连接的目标是服务端Socket。为此,客户端Socket必须首先描述它要连接的服务端Socket,指出服务端Socket的地址和端口号,然后就向服务端Socket提出连接请求

3、连接确认

当服务端Socket监听到或者说是接收到客户端Socket的连接请求,它就响应客户端Socket的请求,建立一个新的线程,把服务端Socket的描述发给客户端,一旦客户端确认了此描述,连接就好了。而服务端Socket继续处于监听状态,继续接收其他客户端套接字的连接请求

Socket,它只是TCP/IP网络的API而已,Socket接口定义了许多函数,用以开发TCP/IP网络上的应用程序,组织数据,以符合指定的协议

www.baidu.com    顶级域名:com    二级域名:baidu   三级域名:www

输入网址到获取页面的网络请求的过程:

(1)域名解析,其实就是根据用户输入的网址去寻找它对应的IP地址,比如输入www.baidu.com的网址就会经历以下过程
1.先从浏览器缓存里找IP,因为浏览器会缓存DNS记录一段时间
2.如没找到,再从Hosts文件查找是否有该域名和对应IP
3.如没找到,再从路由器缓存找
4.如没好到,再从DNS缓存查找
5.如果都没找到,浏览器域名服务器向根域名服务器(baidu.com)查找域名对应IP,还没找到就把请求转发到下一级,直到找到IP
(2)建立TCP连接 (这里使用五层协议更详细的描述如何建立这个TCP链接的)
先是客户端发起请求过程:
 1. 使用应用层发起HTTP请求(这个可以根据你本身输入的url访问时,用的什么协议就发起对应协议去进行请求)
 2. 然后是传输层的TCP协议为传输报文提供可靠的字节流服务,这里也就使用了TCP三次握手
 3. 网络层是把TCP分割好的各种数据包传送给接收方。而要保证确实能传到接收方还需要接收方的MAC地址,也就是物理地址(数据链路层协议:
ARP解析协议) 
 4. 然后才是链路层将数据发送到数据链路层传输。至此请求报文已发出,客户端发送请求的阶段结束
然后是服务端接受请求处理阶段:
原路进行处理:链路层—>网络层—>传输层—>应用层然后响应客户端发送报文。

通信完成后,通过4次挥手,断开TCP的连接、客户端拿到服务端返回的数据

(3)绘制网页
然后浏览器会进行渲染,浏览器根据HTML和CSS计算得到渲染,绘制到屏幕上,js会被执行

过程中使用到的协议:ARP,IP,SOFP
ARP(地址解析协议):主要解决的是同一个局域网内主机或路由器IP地址和MAC地址之间的映射问题。
工作过程:当源主机要发送数据的时候,他会查看自己的ARP高速缓存中是否有目的主机IP地址对应的MAC地址,如果有,则直接发送,如果没有,他就会向本网段所有主机发送ARP请求分组,接到请求的主机查看目的IP与自己的IP是否相等,如果不等就忽略,如果相等,就把源主机的IP和MAC写入自己的ARP高速缓存,如果之前有就覆盖掉,然后把自己的MAC写入ARP相应包,源主机接到ARP响应包后把目的主机的IP和MAC地址写入ARP高速缓存,并且利用此信息发送数据。
路由选择协议
1.内部网关协议
1)RIP协议
 RIP基于UDP的应用层协议,他认为一个好的路由是他通过的路由器少,RIP允许一条路径最多可以包含15个路由,16个即为不可达了。只与自己相邻的路由器交换信息,范围限制在15跳(15度)之内。
 工作过程:假设路由器R0向R1发送一个报文段
                 首先修改R0发来的RIP报文中所有的项目,把一跳字段的地址改为R0,把所有距离字段值加1,
                 对于修改后的RIP报文的每一项进行如下步骤,
                 步骤一、首先看原来的路由表中是否有目的网络N,如果没有直接加入到路由表中,如果有进行步骤2
                 步骤二、然后看下一跳,如果一跳的地址是R0则用新的项目替换原来的项目,如果下一跳不同则进行步骤三
                 步骤三、对比距离,如果距离小于源路由器的项目,那么更新,否则什么都不做。
2)OSPF(最短路径优先)
     OSPF是网络层协议基于IP,当路由的链路状态发生变化时,他会使用洪泛法向本自治系统中所有路由发送自己的的链路状态(链路状态就是自己和那些路由相邻)
IP报头:

IP数据包如何判断是tcp还是udp连接的?协议字段指示上层协议是TCP、UDP、ICMP 还是IGMP 

 

 微信电脑端登录,需要手机验证,中间的通信原理:浏览器获得一个唯一的、临时的uid,通过长连接等待客户端扫描带有此uid的二维码后,从长连接中获得客户端上报给服务器的帐号信息进行展示。并在客户端点击确认后,获得服务器授信的令牌Token,进行随后的信息交互过程。 在超时、网络断开、其他设备上登录后,此前获得的令牌或丢失、或失效,对授权过程形成有效的安全防护。(每次打开微信网页版的时候,都会生成一个含有唯一uid的二维码,而且每次刷新后都会改变。这样可以保证一个uid只可以绑定一个账号和密码,确定登录用户的唯一性。)

 

 

进程和线程的区别:

1、内存空间的区别:

 进程是有独立的内存空间,每个进程之间是相互独立的,互不干扰,

   线程有共享的内存空间(也有私有的)(一个程序内共享)

2、安全性:进程是相互独立的,一个进程的奔溃不会影响到其他的进程,进程是安全的,

线程存在内存空间的共享,一个线程的奔溃可能会影响到其他的线程的执行。,线程的安全性不如进程

关系:进程是相互独立的,一个进程下可以有一个或者多个线程

总结:进程独立、线程可共享;进程安全,线程可能不安全;进程资源分配,线程资源调度。

 超时重传RTT:

即Round Trip Time,表示从发送端到接收端的一去一回需要的时间,tcp在数据传输过程中会对RTT进行采样(即对发送的数据包及其ACK的时间差进行测量,并根据测量值更新RTT值,具体的算法TCPIP详解里面有),TCP根据得到的RTT值更新RTO值,即Retransmission TimeOut,就是重传间隔,发送端对每个发出的数据包进行计时,如果在RTO时间内没有收到所发出的数据包的对应ACK,则任务数据包丢失,将重传数据。

 

 

 

Http请求报文结构
http报文结构由请求行,请求头,空行、请求正文组成(Get请求,没有请求正文)
请求行:请求方法(get/post)、url、版本号
请求头:Host:接收请求的服务器地址,可以是ip也可以是端口号
             User-Agent:发送请求的应用程序名称
             Connection:指定与连接相关的属性,Connection:Keep-Alive
             Accept-Charset:指定可接收的编码格式
             Accept-Encoding:指定可接收的数据压缩格式
             Accept-Language:指定可以接收的语言
空行:表示请求头结束
请求正文:可选,get就没有请求正文

 

Http响应报文结构
http响应报文由状态行、响应头、空行、响应正文四部分组成
状态行:协议版本、状态码、状态描述,之间用空格分开
响应头:Server:服务器应用程序软件的名称和版本号
             Content-Type:相应正文的类型(是图片还是二进制)
             Content-Length:相应正文的长度
             Content-Charset:相应正文的使用编码
             Content-Encoding:相应正文使用的数据压缩格式
             Content-Language:相应正文使用的语言
空行:表示响应头结束
响应正文:

 HTTP是一种应用层协议,提供OSI用户服务,例如事物处理程序、文件传送协议和网络管理等,其目的最终是为了实现应用进程之间的信息交换

 HTTP版本:

HTTP/1.0 默认短连接(一次请求建议一次TCP连接,请求完就断开),支持GET、POST、 HEAD请求

HTTP/1.1 默认长连接(一次TCP连接可以多次请求);支持PUT、DELETE、PATCH等六种请求

        增加host头,支持虚拟主机;支持断点续传功能

HTTP/2.0 多路复用,降低开销(一次TCP连接可以处理多个请求);

    服务器主动推送(相关资源一个请求全部推送);

    解析基于二进制,解析错误少,更高效(HTTP/1.X解析基于文本);

    报头压缩,降低开销。

 

 HTTP和HTTPS区别?

  1. HTTP 是超文本传输协议,信息是明文传输,存在安全风险的问题。HTTPS 则解决 HTTP 不安全的缺陷,在 TCP 和 HTTP 网络层之间加入了 SSL/TLS 安全协议,使得报文能够加密传输。

  2. HTTP 连接建立相对简单, TCP 三次握手之后便可进行 HTTP 的报文传输。而 HTTPS 在 TCP 三次握手之后,还需进行 SSL/TLS 的握手过程,才可进入加密报文传输。

  3. HTTP 的端口号是 80,HTTPS 的端口号是 443。

  4. HTTPS 协议需要向 CA(证书权威机构)申请数字证书,来保证服务器的身份是可信的。

HTTPS优势:

  • 信息加密:交互信息无法被窃取,但你的号会因为「自身忘记」账号而没。

  • 校验机制:无法篡改通信内容,篡改了就不能正常显示,但百度「竞价排名」依然可以搜索垃圾广告。

  • 身份证书:证明淘宝是真的淘宝网,但你的钱还是会因为「剁手」而没。

  • 混合加密的方式实现信息的机密性,解决了窃听的风险。

  • 摘要算法的方式来实现完整性,它能够为数据生成独一无二的「指纹」,指纹用于校验数据的完整性,解决了篡改的风险将服务器公钥放入到数字证书中,解决了冒充的风险。

https传输过程:客户端发起 HTTPS 请求,服务端返回证书,客户端对证书进行验证,验证通过后本地生成用于改造对称加密算法的随机数。
通过证书中的公钥对随机数进行加密传输到服务端,服务端接收后通过私钥解密得到随机数,之后的数据交互通过对称加密算法进行加解密。

HTTP Get 和 Post 区别:

get 方法一般用于请求,比如你在浏览器地址栏输入 www.cxuanblog.com 其实就是发送了一个 get 请求,它的主要特征是请求服务器返回资源,而 post 方法一般用于表单的提交,相当于是把信息提交给服务器,等待服务器作出响应,get 相当于一个是 pull/拉的操作,而 post 相当于是一个 push/推的操作。

get 方法是不安全的,因为你在发送请求的过程中,你的请求参数会拼在 URL 后面,从而导致容易被攻击者窃取,对你的信息造成破坏和伪造;而 post 方法是把参数放在请求体 body 中的,这对用户来说不可见。

get 请求的 URL 有长度限制,而 post 请求会把参数和值放在消息体中,对数据长度没有要求。

get 请求在发送过程中会产生一个 TCP 数据包post 在发送过程中会产生两个 TCP 数据包。对于 get 方式的请求,浏览器会把 http header 和 data 一并发送出去,服务器响应 200(返回数据);而对于 post,浏览器先发送 header,服务器响应 100 continue,浏览器再发送 data,服务器响应 200 ok(返回数据)。

 

请求方法:

HTTP1.0定义了三种请求方法:GET、POST、HEAD

HTTP1.1定义了六种请求方法:PUT、DELETE、PATCH、OPTIONS、CONNECT、TRACE 

GET:请求获取Request——URL所标识的资源

POST:在Request——URL所标识的资源后附加资源

HEAD:请求获取由Request——URL所标识的资源的响应消息报头

PUT:请求服务器存储一个资源,由Request——URL作为其标识

DELETE:请求服务器删除由Request——URL所标识的资源

TRACE:请求服务器回送收到的请求信息

CONNECT:保留,要求用隧道协议进行TCP通信

OPTIONS:请求查询服务器性能;查询针对URI请求指定资源的方法

PATCH: 部分更新指定资源的数据(非幂等)

HTTP 就是一种无状态的协议,他对用户的操作没有记忆能力。用 Cookie 技术。

http版本更新: 

版本产生时间内容 
HTTP/0.9 1991年 不涉及数据包传输,规定客户端和服务器之间通信格式,只能GET请求  
HTTP/1.0 1996年 传输内容格式不限制,增加POST、HEAD命令,短连接  
HTTP/1.1 1997年

持久连接(Connetion:keep-alive连接)、

断点传送(客户端可以要求服务端从上次传送的字节接着传输)、

支持HOST域(WEB浏览器可以使用主机头名来明确表示要访问服务器上的哪个WEB站点)、

分块传输编码(chunked编码)、

请求流水线Pipelining(管道机制:请求能够并行传输;

服务器必须按照客户端请求的先后顺序依次回送相应的结果,以保证客户端能够区分出每次请求的响应内容

缓存处理:HTTP/1.1在1.0的基础上加入了一些cache的新特性,引入了实体标签,一般被称为e-tags,新增更为强大的Cache-Control头

 
HTTP/2 2015年 多路复用(连接发起多重的请求-响应)、服务器推送(提前push css和耗时的API推送(比如路由关联))、头信息压缩(HPACK算法
)、二进制分帧(将传输数据分为二进制时分为帧,可交错传输数据;把原来HTTP1.x的header和body部分用frame重新封装了一层而已。

)等

 

 
 
多路复用:每个数据流以消息的形式发送,而消息由一或多个帧组成。这些帧可以乱序发送,然后再根据每个帧首部的流标识符(stream id)重新组装。

http 响应码 301 和 302 代表的是什么?有什么区别?

答:301,302 都是HTTP状态的编码,都代表着某个URL发生了转移。

区别:

301 redirect: 301 代表永久性转移(Permanently Moved)。
302 redirect: 302 代表暂时性转移(Temporarily Moved )。

 

1xx (临时响应):表示临时响应并需要请求者继续执行操作的状态代码。

4xx 类状态码:表示客户端发送的报文有误,服务器无法处理,也就是错误码的含义。

404 Not Found」表示请求的资源在服务器上不存在或未找到,所以无法提供给客户端。401代表访问的页面没有授权,403表示没有权限访问这个页面,404代表没有这个页面

5xx 类状态码表示客户端请求报文正确,但是服务器处理时内部发生了错误,属于服务器端的错误码。500代表服务器内部异常,504代表服务器端超时,没返回结果

 

Host:客户端发送请求时,用来指定服务器的域名。有了 Host 字段,就可以将请求发往「同一台」服务器上的不同网站。

 

 cookie和session:1、cookie数据存放在客户的浏览器上,session数据放在服务器上.

2、cookie不是很安全,别人可以分析存放在本地的COOKIE并进行COOKIE欺骗考虑到安全应当使用session。

3、设置cookie时间可以使cookie过期。但是使用session-destory(),我们将会销毁会话。

4、session会在一定时间内保存在服务器上。当访问增多,会比较占用你服务器的性能考虑到减轻服务器性能方面,应当使用cookie。

5、单个cookie保存的数据不能超过4K,很多浏览器都限制一个站点最多保存20个cookie。(Session对象没有对存储的数据量的限制,其中可以保存更为复杂的数据类型)

 两者最大的区别在于生存周期,一个是IE启动到IE关闭.(一个会话),一个是预先设置的生存周期,或永久的保存于本地的文件。(cookie)

 

10.读过的书? 大话数据结构、java并发编程的艺术、MYSQL入门很简单、码农翻身、深入理解java虚拟机

11.java基础:

Java中的对象创建有多少种方式?

1.Java中有很多方式可以创建一个对象,最简单的方式就是使用new关键字。

User user = new User();

2.除此以外,还可以使用反射机制创建对象:

User user = User.class.newInstance();

3.或者使用Constructor类的newInstance:

Constructor<User> constructor = User.class.getConstructor();

User user = constructor.newInstance();

 

String StringBuilder StringBuffer区别:

Java中对String对象进行的操作实际上是一个不断创建新的对象并且将旧的对象回收的一个过程,所以执行速度很慢。在线程安全上,StringBuilder是线程不安全的,而StringBuffer是线程安全的(synchronized关键字)。

Java的反射,你目前主要用他做什么,以及Java的泛型,他的主要作用是什么

JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射机制。Java反射可以用来获取一个class对象或实例化一个class表示的类的对象,还可以获取构造方法,成员变量,成员方法。

java中泛型的引入主要是为了解决两个方面的问题:1.集合类型元素在运行期出现类型装换异常,增加编译时类型的检查,2. 解决的时重复代码的编写,能够复用算法。

封装,即在对外部隐藏对象的属性和实现的细节,仅仅给其他的类提供公开的访问方法。提高了数据的安全性,提高了代码的复用性。

继承,即类A可以不用重新编写类B中的属性和方法就可以实现对类B的代码的重用,并能扩展新的属性和方法。

多态,基类的引用,引用不同的派生类对象,调用基类和派生类的同名覆盖方法,基类引用哪个派生类对象,就会调用哪个派生类对象的方法,这是多态的基本的使用场景。

重载,从简单说,就是函数或者方法有相同的名称,但是参数列表不相同的情形,这样的同名不同参数的函数或者方法之间,互相称之为重载函数或者方法。方便了程序员可以根据不同的参数个数,类型,自动匹配方法,减少写多个函数名或方法名的重复步骤。

 

抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类

描述抽象类和接口的区别

只能继承一个抽象类;可以实现多个接口;抽象类的子类使用 extends 来继承;接口必须使用 implements 来实现接口;

抽象类可以定义类变量和实例变量,类方法和实例方法,抽象方法;

拥有抽象方法的类 一定是抽象类 ,抽象类不一定拥有抽象方法;接口定义的变量都是public static final修饰的,定义的方法都是public abstract修饰的,而且不能提供代码实现;Java8开始,接口也可以通过定义default方法,给方法提供实现

一般把所有派生类公共的重写方法应该定义在抽象类里面,而把派生类特有的功能方法,放在接口当中定义。

简单类型和对应的包装类型:

 *  byte => Byte      1
 *  short => Short    2
 *  int => Integer     4
 *  long => Long      8
 *  float => Float
 *  double => Double
 *  char => Character
 *  boolean => Boolean

java int 类整数的最大值是 2 的 31 次方 - 1 = 2147483648 - 1 = 2147483647(有一位符号位)

可以用 Integer.MAX_VALUE 表示它,即 int value = Integer.MAX_VALUE;

JDK:Java Development Kit 的简称,java 开发工具包,提供了 java 的开发环境和运行环境。
JRE:Java Runtime Environment 的简称,java 运行环境,为 java 的运行提供了所需环境。
具体来说 JDK 其实包含了 JRE,同时还包含了编译 java 源码的编译器 javac,还包含了很多 java 程序调试和分析的工具。简单来说:如果你需要运行 java 程序,只需安装 JRE 就可以了,如果你需要编写 java 程序,需要安装 JDK。

Integer和int的区别:

1、integer是int的包装类,int是Java的一种基本数据结构

2、integer变量必须实例化后才能使用,int变量不需要

3、integer实际是对象的引用,int是直接存储数据值

4、integer的默认值是null,int的默认值是0

String类常用方法? length()、equals() equalsgnoreCase()、startsWith() endsWith()、indexOf()、replace()、trim()去掉首尾空格、valueOf()转化为字符串、spilt()、substring方法可以提取字符串中的子串、charAt(int index)返回字符串中指定位置的字符

浅拷贝和深拷贝:

浅拷贝:list本质上是数组,而数组的是以地址的形式进行存储。
(listA中有一个元素Object)将list A浅拷贝给list B,由于进行的是浅拷贝,所以直接将A的内容复制给了B,虽然listA和listB所在的地址不一样,但是两个list存储的对象仍然是同一个Object(因为把listA复制到listB时,浅拷贝不是复制Object到B,而是把Object的引用复制到B,所以两个地址仍指向同一个Object),导致改变在listA中改变Object的值时,listB中的也会改变。

深拷贝:是将A复制给B的同时,再将地址A存储的对象拷贝传递到地址B的引用。ListA与ListB内容一致,但是由于地址中引用的对象不同,所以改变listA中的Object不会影响listB中的Object,因为存储的对象已经不一样,只是两个对象的值一样,相互不受影响。

对象的生命周期:

 创建阶段(Creation)、应用阶段(Using)、不可视阶段(Invisible)、不可到达阶段(Unreachable)、可收集阶段(Collected)、终结阶段(Finalized)与释放阶段(Free)

1.创建阶段:在对象创建阶段,系统要通过下面的步骤,完成对象的创建过程:(1)为对象分配存储空间。(2)开始构造对象。(3)递归调用其超类的构造方法。(4)进行对象实例初始化与变量初始化。(5)执行构造方法体。

2.应用阶段(In Use):对象至少被一个强引用持有着。

3.不可见阶段(Invisible):当一个对象处于不可见阶段时,说明程序本身不再持有该对象的任何强引用,虽然该这些引用仍然是存在着的。简单说就是程序的执行已经超出了该对象的作用域了。

4.不可达阶段(Unreachable):对象处于不可达阶段是指该对象不再被任何强引用所持有。与“不可见阶段”相比,“不可见阶段”是指程序不再持有该对象的任何强引用,这种情况下,该对象仍可能被JVM等系统下的某些已装载的静态变量或线程或JNI等强引用持有着,这些特殊的强引用被称为”GC root”。存在着这些GC root会导致对象的内存泄露情况,无法被回收。

 5.收集阶段(Collected):当垃圾回收器发现该对象已经处于“不可达阶段”并且垃圾回收器已经对该对象的内存空间重新分配做好准备时,则对象进入了“收集阶段”。如果该对象已经重写了finalize()方法,则会去执行该方法的终端操作。

这里要特别说明一下:不要重载finazlie()方法!原因有两点:

  • l  会影响JVM的对象分配与回收速度

在分配该对象时,JVM需要在垃圾回收器上注册该对象,以便在回收时能够执行该重载方法;在该方法的执行时需要消耗CPU时间且在执行完该方法后才会重新执行回收操作,即至少需要垃圾回收器对该对象执行两次GC。

  • l  可能造成该对象的再次“复活”

在finalize()方法中,如果有其它的强引用再次持有该对象,则会导致对象的状态由“收集阶段”又重新变为“应用阶段”。这个已经破坏了Java对象的生命周期进程,且“复活”的对象不利用后续的代码管理。

 6.终结阶段:当对象执行完finalize()方法后仍然处于不可达状态时,则该对象进入终结阶段。在该阶段是等待垃圾回收器对该对象空间进行回收。

7.对象空间重新分配阶段:垃圾回收器对该对象的所占用的内存空间进行回收或者再分配了,则该对象彻底消失了,称之为“对象空间重新分配阶段”。

final 在 java 中有什么作用?

final 修饰的类叫最终类,该类不能被继承。final 修饰的方法不能被重写。final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。

String 和 StringBuffer、StringBuilder 的区别在于 String 声明的是不可变的对象,每次操作都会生成新的 String 对象,然后将指针指向新的 String 对象,而 StringBuffer、StringBuilder 可以在原有对象的基础上进行操作,所以在经常改变字符串内容的情况下最好不要使用 String。StringBuffer 和 StringBuilder 最大的区别在于,StringBuffer 是线程安全的,而 StringBuilder 是非线程安全的,但 StringBuilder 的性能却高于 StringBuffer,所以在单线程环境下推荐使用 StringBuilder,多线程环境下推荐使用 StringBuffer。

Java中的异常处理机制?

在Java 应用程序中,异常处理机制为:抛出异常,捕捉异常。

抛出异常:当一个方法出现错误引发异常时,方法创建异常对象并交付运行时系统,异常对象中包含了异常类型和异常出现时的程序状态等异常信息。运行时系统负责寻找处置异常的代码并执行。

捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适 的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。同时,意味着Java程序的终止。

对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同:

由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。

对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。

对于所有的可查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。

通常使用关键字try、catch、finally来捕获异常:

try块:用于捕获异常。其后可接零个或多个catch块,如果没有catch块,则必须跟一个finally块。

catch块:用于处理try捕获到的异常。

finally块:无论是否捕获或处理异常,finally块里的语句都会被执行。当在try块或catch块中遇到return语句时,finally语句块将在方法返回之前被执行。在以下4种特殊情况下,finally块不会被执行:

1)在finally语句块中发生了异常。

2)在前面的代码中用了System.exit()退出程序。

3)程序所在的线程死亡。

4)关闭CPU。

try、catch、finally语句块的执行顺序:

1)当try没有捕获到异常时:try语句块中的语句逐一被执行,程序将跳过catch语句块,执行finally语句块和其后的语句;

2)当try捕获到异常,catch语句块里没有处理此异常的情况:当try语句块里的某条语句出现异常时,而没有处理此异常的catch语句块时,此异常将会抛给JVM处理,finally语句块里的语句还是会被执行,但finally语句块后的语句不会被执行;

3)当try捕获到异常,catch语句块里有处理此异常的情况:在try语句块中是按照顺序来执行的,当执行到某一条语句出现异常时,程序将跳到catch语句块,并与catch语句块逐一匹配,找到与之对应的处理程序,其他的catch语句块将不会被执行,而try语句块中,出现异常之后的语句也不会被执行,catch语句块执行完后,执行finally语句块里的语句,最后执行finally语句块后的语句;

throw 和 throws 的区别?

throws是用来声明一个方法可能抛出的所有异常信息,throws是将异常声明但是不处理,而是将异常往上传,谁调用我就交给谁处理。而throw则是指抛出的一个具体的异常类型。

常见的异常类有哪些?

NullPointerException:当应用程序试图访问空对象时,则抛出该异常。
SQLException:提供关于数据库访问错误或其他错误信息的异常。
IndexOutOfBoundsException:指示某排序索引(例如对数组、字符串或向量的排序)超出范围时抛出。
NumberFormatException:当应用程序试图将字符串转换成一种数值类型,但该字符串不能转换为适当格式时,抛出该异常。
FileNotFoundException:当试图打开指定路径名表示的文件失败时,抛出此异常。
IOException:当发生某种I/O异常时,抛出此异常。此类是失败或中断的I/O操作生成的异常的通用类。
ClassCastException:当试图将对象强制转换为不是实例的子类时,抛出该异常。
IllegalArgumentException:抛出的异常表明向方法传递了一个不合法或不正确的参数。
ArithmeticException:当出现异常的运算条件时,抛出此异常。例如,一个整数“除以零”时,抛出此类的一个实例。
NoSuchMethodException:无法找到某一特定方法时,抛出该异常。
UnsupportedOperationException:当不支持请求的操作时,抛出该异常。
RuntimeExceptionRuntimeException:是那些可能在Java虚拟机正常运行期间抛出的异常的超类。

在Java中,异常类的结构层次图如下图所示:

        在Java中,所有异常类的父类是Throwable类,Error类是error类型异常的父类,Exception类是exception类型异常的父类,RuntimeException类是所有运行时异常的父类.

   Error : StackOverFlow OOM

        典型的RuntimeException包括NullPointerException、IndexOutOfBoundsException、IllegalArgumentException等。

        典型的非RuntimeException包括IOException(FileNotFoundException)、SQLException等。

Java如何实现跨平台

作为Java 程序员的我们只需要写一堆 ***.java 文件,编译器把 .java 文件编译成 .class 字节码文件,后面的事就都交给Java 虚拟机(JVM)做了。如下图所示, Java虚拟机是区分平台的,虚拟机来进行 .class 字节码指令翻译成平台相关的机器码。所以 Java 是跨平台的,Java 虚拟机(JVM)不是跨平台的,JVM 是平台相关的。

Object类的方法:

clone:保护方法,实现对象的浅复制,只有实现了Cloneable接口才可以调用该方法,否则抛出CloneNotSupportedException异常

equals:在Object中与==是一样的,子类一般需要重写该方法

hashCode:该方法用于哈希查找,重写了equals方法一般都要重写hashCode方法。这个方法在一些具有哈希功能的Collection中用到

getClass:final方法,获得运行时类型

wait:使当前线程等待该对象的锁,当前线程必须是该对象的拥有者,也就是具有该对象的锁。wait()方法一直等待,直到获得锁或者被中断。wait(long timeout)设定一个超时间隔,如果在规定时间内没有获得锁就返回。

调用该方法后当前线程进入睡眠状态,直到以下事件发生:
1. 其他线程调用了该对象的notify方法
2. 其他线程调用了该对象的notifyAll方法
3. 其他线程调用了interrupt中断该线程
4. 时间间隔到了
此时该线程就可以被调度了,如果是被中断的话就抛出一个InterruptedException异常

notify:唤醒在该对象上等待的某个线程

notifyAll:唤醒在该对象上等待的所有线程

toString:转换成字符串,一般子类都有重写,否则打印句柄、

 

java四大修饰符:

public:        当前类   同包  不同包子类  不同包非子类
protected:  当前类   同包  不同包子类
default:      当前类   同包  
private:      当前类

请问如何判断两个链表是否相交

从头遍历两个链表。创建两个栈,第一个栈存储第一个链表的节点,第二个栈存储第二个链表的节点。每遍历到一个节点时,就将该节点入栈。两个链表都入栈结束后。则通过top判断栈顶的节点是否相等即可判断两个单链表是否相交。因为我们知道,若两个链表相交,则从第一个相交节点开始,后面的节点都相交。 若两链表相交,则循环出栈,直到遇到两个出栈的节点不相同,则这个节点的后一个节点就是第一个相交的节点。

12.数据库

sql语句:

1.查找每门课都大于70的学生

SELECT DISTINCT Sname 
FROM student s,sc c 
WHERE Sname NOT IN(SELECT DISTINCT Sname 
FROM student s, sc c 
WHERE s.`Sid`=c.`Sid` AND c.score < 70) AND s.`Sid`=c.`Sid`; //and条件为了防止出现没有成绩的学生
SELECT Sname,score
FROM sc,student
WHERE sc.`Sid`=student.`Sid` 
GROUP BY Sname  HAVING  MIN(score) >70

2. 数据库:查询课程号为2成绩前三名的同学

SELECT Sname
FROM sc,student
WHERE sc.`Sid`=student.`Sid` AND CID=1 
ORDER BY score DESC LIMIT 3

3.求shu课成绩最高的学生的名字,注意limit和in查询不能一起用

SELECT Sname FROM student WHERE student.`Sid` IN 
(SELECT sid FROM(SELECT sid,score FROM sc,course  WHERE course.`CID`=sc.`CID` AND course.`Cname`='shu' ORDER BY score DESC LIMIT 1)AS s)

4.查询每门课程的平均成绩,结果按平均成绩降序排序

SELECT Cname,AVG(score) FROM sc,course 
WHERE sc.`CID`=course.`CID`
GROUP BY sc.cid 
ORDER BY score DESC

5.sql,一张表,一条语句,得到成绩>=60和<60的人数,不能使用两条语句分别查询

 

select class 班级,
sum(case when score>=60 then 1 else 0 end) as 及格人数,
sum(case when score<60 then 1 else 0 end) as 不及格人数
from tb1
group by class;

 

 

 

 

Sql执行顺序:

from  on  join   where   groupby   having   select      orderby   limit

 

连接查询 

范式:

第一范式:1NF是对属性的原子性约束,要求属性具有原子性,不可再分解;
第二范式:2NF是对记录的惟一性约束,要求记录有惟一标识,即实体的惟一性;
非主键列不存在对主键的部分依赖 (要求每个表只描述一件事情)
第三范式:3NF是不存在对非主键列的传递依赖
打个比方,比如评论表,如果你将用户ID,用户头像都放在这留言表中,就是不合适的了。用户头像是依赖于用户ID,而不依赖该评论。

 事务如何实现的?
(1):通过预写日志方式实现的,redo和undo机制是数据库实现事务的基础
(2):redo日志用来在断电/数据库崩溃等状况发生时重演一次刷数据的过程,把redo日志里的数据刷到数据库里,保证了事务 的持久性(Durability)
(3):undo日志是在事务执行失败的时候撤销对数据库的操作,保证了事务的原子性

不隔离可能的发生:脏读、不可重复性(前后读到不一样的数据)、虚读(幻读,有新增或者删除数据)

隔离级别:读未提交、读已提交、可重复读(默认)、串行化

四大特性:原子性(保证:事务发送错误会进行回滚)、隔离性、持久性、一致性

order by 和 group by 的区别:
1,order by 从英文里理解就是行的排序方式,默认的为升序。 order by 后面必须列出排序的字段名,可以是多个字段名。
2,group by 从英文里理解就是分组。必须有“聚合函数”来配合才能使用,使用时至少需要一个分组标志字段。
注:即先对select xx from xx的记录集合用where进行筛选,然后再使用group by 对筛选后的结果进行分组 使用having字句对分组后的结果进行筛选

 

delete 、drop 、truncate:

当你不再需要该表时, 用 drop;当你仍要保留该表,但要删除所有记录时, 用 truncate;当你要删除部分记录时(always with a WHERE clause), 用 delete.

drop、truncate会隐式提交,不能回滚,不会触发触发器;delete,执行delete操作时,每次从表中删除一行,并且同时将该行的的删除操作记录在redo和undo表空间中以便进行回滚(rollback)和重做操作,需要手动提交(commit)操作才能生效,可以通rollback撤消操作。

 

数据库语句的一些关键字:增加create 删除drop 修改alter update set  查select  查前10行limit 0,10

主键和外键的区别:
1.主键是能确定一条记录的唯一标识,比如,一条记录包括身份正号,姓名,年龄。

身份证号是唯一能确定你这个人的,其他都可能有重复,所以,身份证号是主键。
2.外键用于与另一张表的关联。是能确定另一张表记录的字段,用于保持数据的一致性。

底层为什么为B+树?与hash索引区别?

哈希索引就是采用一定的哈希算法,把键值换算成新的哈希值,检索时不需要类似B+树那样从根节点到叶子节点逐级查找,只需一次哈希算法即可立刻定位到相应的位置,速度非常快。

从上面的图来看,B+树索引和哈希索引的明显区别是:

  • 如果是等值查询,那么哈希索引明显有绝对优势,因为只需要经过一次算法即可找到相应的键值;当然了,这个前提是,键值都是唯一的。如果键值不是唯一的,就需要先找到该键所在位置,然后再根据链表往后扫描,直到找到相应的数据;
  • 从示意图中也能看到,如果是范围查询检索,这时候哈希索引就毫无用武之地了,因为原先是有序的键值,经过哈希算法后,有可能变成不连续的了,就没办法再利用索引完成范围查询检索;B+树节点是天然有序
  • 同理,哈希索引也没办法利用索引完成排序,以及like ‘xxx%’ 这样的部分模糊查询(这种部分模糊查询,其实本质上也是范围查询);
  • 哈希索引也不支持多列联合索引的最左匹配规则
  • B+树索引的关键字检索效率比较平均,不像B树那样波动幅度大,在有大量重复键值情况下,哈希索引的效率也是极低的,因为存在所谓的哈希碰撞问题

通常,B+树索引结构适用于绝大多数场景,像下面这种场景用哈希索引才更有优势:

在HEAP表中,如果存储的数据重复度很低(也就是说基数很大),对该列数据以等值查询为主,没有范围查询、没有排序的时候,特别适合采用哈希索引

例如这种SQL:
SELECT … FROM t WHERE C1 = ?; — 仅等值查询

在大多数场景下,都会有范围查询、排序、分组等查询特征,用B+树索引就可以了。

 B+树和B-树?

1.B-树的每一个节点,存了关键字和对应的数据地址,而B+树的非叶子节点只存关键字,不存数据地址
因此B+树的每一个非叶子节点存储的关键字是远远多于B-树的,B+树的叶子节点存放关键字和数据,
因此,从树的高度上来说,B+树的高度要小于B-树,使用的磁盘I/O次数少,
因此查询会更快一些。
2、B-树由于每个节点都存储关键字和数据,因此离根节点近的数据,查询的就快,离根节点远的数据,查询的就慢;
B+树所有的数据都存在叶子节点上,因此在B+树上搜索关键字,找到对应数据的时间是比较平均的,没有快慢之分。
3、在B-树上如果做区间查找,遍历的节点是非常多的;B+树所有叶子节点被连接成了有序链表结构,因此做整表遍历和区间查找是非常容易的。

索引注意事项:需要查询,排序,分组和联合操作的字段适合建立索引;索引多,数据更新表越慢,尽量使用字段值不重复比例大的字段作为索引,联合索引比多个独立索引效率高;对数据进行频繁查询进建立索引,如果要频繁更改数据不建议使用索引;

索引弊端:
1. 占用磁盘空间.
2. 对dml(插入,修改.删除)操作有影响,变慢
使用场景:
1. 肯定在where条件经常使用,如果不做查询就没有意义
2. 该字段的内容不是唯一的几个值(sex).
3. 字段内容不是频繁变化

具体技巧:
1. 对于创建的多列索引(复合索引),不是使用的第一部分就不会使用索引(最左匹配)
2. 对于使用like查询,查询如果是”%aaa”不会使用到索引,而”aaa%”会使用到索引
3. 如果条件中有or,有条件没有使用索引,即使其中有条件带索引,也不会使用.简单来说,就是要求使用的所有字段,都必须单独使用时才能使用索引.
4. 如果列类型是字符串,拿一定要在条件中将数据使用引号引用起来,否则索引失效
5. 如果mysql估计使用全表扫描要比索引快,则不适用索引.例子:表里只有一条记录

 MyISAM 表格将在哪里存储,并且还提供其存储格式?

每个 MyISAM 表格以三种格式存储在磁盘上:
·“.frm”文件存储表定义
·数据文件具有“.MYD”(MYData)扩展名
索引文件具有“.MYI”(MYIndex)扩展名

myisam 引擎 ---- frm(定义的表结构) + myd(表中的数据记录) + myi(表中的索引)     (数据与索引分离)

           不支事物、外键,优势是访问速度快

innodb 引擎  --- frm(表的创建结构存储) + idb(表中数据和索引的存储)                                       (数据+索引)

          是mysql默认的存储引擎,具有事物特征,支持外键,支持自动增长列

内连接和外连接:内连接,也被称为自然连接,只有两个表相匹配的行才能在结果集中出现。返回的结果集选取了两个表中所有相匹配的数据,舍弃了不匹配的数据。外连接除了显示符合条件的记录外,还显示表中的记录。

数据库查询慢的原因?

        1、没有索引或者没有用到索引(这是查询慢最常见的问题,是程序设计的缺陷) 
        2、没有创建计算列导致查询不优化。 
        3、内存不足 
        4、网络速度慢 
        5、查询出的数据量过大(可以采用多次查询,其他的方法降低数据量) 
        6、锁或者死锁(这也是查询慢最常见的问题,是程序设计的缺陷) 
        7、sp_lock,sp_who,活动的用户查看,原因是读写竞争资源。 
        8、返回了不必要的行和列 
        9、查询语句不好,没有优化 

数据库优化?

1.选择更小的数据类型,用TinyInt(8位)不用Int(32位)

2.建立索引的原则使用where order by,group by条件后的字段,并且符合最左匹配原则;建立联合索引;注意索引的失效!=,not in 及Sql语句,适用于where和group by查询时有效。

3.查询时避免无关列,使用连接查询代替子查询。

数据库优化方面的事情

定位:查找,定位慢查询,并优化
优化
.创建索引:创建合适的索引,我们就可以在索引中查询,查询到以后直接找对应的额记录
二,分表:当一张表的数据比较多或者一张表的某些字段的值比较多并且很少使用时,采用水平分表

(水平分表策略:
1. 按时间分表:这种分表方式有一定的局限性,当数据有较强的时效性.如微博发布纪录,微信消息纪录等,这种数据很少会有用户查询几个月前的数据,这时可以按月分表
2. 按区间范围分表:一般在有严格的自增id需求上,如按照user_id水平分表
3. Hash分表(用的多):通过一个原始目标的id或者名称通过一定的hash算法计算出数据库存储表的表名,然后访问相应的表)或垂直分表来优化,比如将字段比较长的(商品详情信息)进行分表,用外键建立联系。
三.读写分离:当一台服务器不能满足需要时,采用将读写分离的方式进行集群
四.缓存:使用redis来进行缓存

若没有执行索引,问题出在哪?sql问题,对索引字段进行了函数操作;连接查询时两个表编码格式不同;字段类型不同;索引统计信息有误,analyse table重新统计;表太多。

索引分类:分类:普通索引,唯一索引,主键索引,全文索引
1.普通索引:允许重复的值出现
2.唯一索引:除了不能有重复的记录外,其它和普通索引一样.(用户名;用户身份证;手机号)
3.主键索引:是随着设定主键而创建的;也就是把某个列设为主键的时候,数据库就会给该列创建索引;唯一且没有null值
4.全文索引:用来对表中文本域(char,varchar,text)进行索引,全文索引针对myisam

普通索引和唯一索引:

对于唯一索引来说,找到3和5之间的位置,判断到没有冲突,插入这个值,语句执行结束;

对于普通索引来说,找到3和5之间的位置,插入这个值,语句执行结束。

这样看来,普通索引和唯一索引对更新语句性能影响的差别,只是一个判断,只会耗费微小的CPU时间。

第二种情况是,这个记录要更新的目标页不在内存中。这时,InnoDB的处理流程如下:

对于唯一索引来说,需要将数据页读入内存,判断到没有冲突,插入这个值,语句执行结束;

对于普通索引来说,则是将更新记录在change buffer,语句执行就结束了。

将数据从磁盘读入内存涉及随机IO的访问,是数据库里面成本最高的操作之一。change buffer因为减少了随机磁盘访问,所以对更新性能的提升是会很明显的。

 

change buffer的使用场景

使用change buffer对更新过程的加速作用,也清楚了change buffer只限于用在普通索引的场景下,而不适用于唯一索引。那么,现在有一个问题就是:普通索引的所有场景,使用change buffer都可以起到加速作用吗?

 

因为merge的时候是真正进行数据更新的时刻,而change buffer的主要目的就是将记录的变更动作缓存下来,所以在一个数据页做merge之前,change buffer记录的变更越多(也就是这个页面上要更新的次数越多),收益就越大。

 

因此,对于写多读少的业务来说,页面在写完以后马上被访问到的概率比较小,此时change buffer的使用效果最好。这种业务模型常见的就是账单类、日志类的系统。

 

反过来,假设一个业务的更新模式是写入之后马上会做查询,那么即使满足了条件,将更新先记录在change buffer,但之后由于马上要访问这个数据页,会立即触发merge过程。这样随机访问IO的次数不会减少,反而增加了change buffer的维护代价。所以,对于这种业务模式来说,change buffer反而起到了副作用。

 

索引选择和实践

其实,这两类索引在查询能力上是没差别的,主要考虑的是对更新性能的影响。所以,我建议你尽量选择普通索引。

 

如果所有的更新后面,都马上伴随着对这个记录的查询,那么你应该关闭change buffer。而在其他情况下,change buffer都能提升更新性能。

 

在实际使用中,你会发现,普通索引和change buffer的配合使用,对于数据量大的表的更新优化还是很明显的。

 

 

多个单列索引在多条件查询时优化器会选择最优索引策略,可能只用一个索引,也可能将多个索引全用上! 但多个单列索引底层会建立多个B+索引树,比较占用空间,也会浪费一定搜索效率,故如果只有多条件联合查询时最好建联合索引
最左前缀原则:
顾名思义是最左优先,以最左边的为起点任何连续的索引都能匹配上,
注:如果第一个字段是范围查询需要单独建一个索引
注:在创建联合索引时,要根据业务需求,where子句中使用最频繁的一列放在最左边。这样的话扩展性较好
同时存在联合索引和单列索引(字段有重复的),这个时候查询mysql会怎么用索引呢?
这个涉及到mysql本身的查询优化器策略了,当一个表有多条索引可走时,  Mysql  根据查询语句的成本来选择走哪条索引;

查询优化器 一条SQL语句的查询,可以有不同的执行方案,至于最终选择哪种方案,需要通过优化器进行选择,选择执行成本最低的方案。 在一条单表查询语句真正执行之前,MySQL的查询优化器会找出执行该语句所有可能使用的方案,对比之后找出成本最低的方案。这个成本最低的方案就是所谓的执行计划。

优化过程大致如下: 1、根据搜索条件,找出所有可能使用的索引    2、计算全表扫描的代价

3、计算使用不同索引执行查询的代价 4、对比各种执行方案的代价,找出成本最低的那一个

联合索引本质:
当创建**(a,b,c)联合索引时,相当于创建了(a)单列索引**,(a,b)联合索引以及**(a,b,c)联合索引**
想要索引生效的话,只能使用 a和a,b和a,b,c三种组合;当然,我们上面测试过,a,c组合也可以,但实际上只用到了a的索引,c并没有用到!
注:这个可以结合上边的 通俗理解 来思考!
其他知识点:
1、需要加索引的字段,要在where条件中
2、数据量少的字段不需要加索引;因为建索引有一定开销,如果数据量小则没必要建索引(速度反而慢)
3、避免在where子句中使用or来连接条件,因为如果俩个字段中有一个没有索引的话,引擎会放弃索引而产生全表扫描
4、联合索引比对每个列分别建索引更有优势,因为索引建立得越多就越占磁盘空间,在更新数据的时候速度会更慢。另外建立多列索引时,顺序也是需要注意的,应该将严格的索引放在前面,这样筛选的力度会更大,效率更高。

索引查询很慢怎么办? force index强制走索引,业务应急预案(不推荐)。覆盖索引+最左匹配原则,删掉选错索引。

 

13.操作系统:

操作系统是运行在计算机上最重要的一种软件,它管理计算机的资源和进程以及所有的硬件和软件。它为计算机硬件和软件提供了一种中间层

操作系统是一种软件,它的主要目的有三种

  • 管理计算机资源,这些资源包括 CPU、内存、磁盘驱动器、打印机等。

  • 提供一种图形界面,就像我们前面描述的那样,它提供了用户和计算机之间的桥梁。

  • 为其他软件提供服务,操作系统与软件进行交互,以便为其分配运行所需的任何必要资源。

 

什么是按需分页

在操作系统中,进程是以页为单位加载到内存中的,按需分页是一种虚拟内存的管理方式。

什么是实时系统

实时操作系统对时间做出了严格的要求,实时操作系统分为两种:硬实时和软实时

硬实时操作系统规定某个动作必须在规定的时刻内完成或发生,比如汽车生产车间,焊接机器必须在某一时刻内完成焊接,焊接的太早或者太晚都会对汽车造成永久性伤害。软实时操作系统虽然不希望偶尔违反最终的时限要求,但是仍然可以接受。并且不会引起任何永久性伤害。比如数字音频、多媒体、手机都是属于软实时操作系统。

你可以简单理解硬实时和软实时的两个指标:是否在时刻内必须完成以及是否造成严重损害。

 

什么是虚拟内存

虚拟内存是一种内存分配方案,是一项可以用来辅助内存分配的机制。我们知道,应用程序是按页装载进内存中的。但并不是所有的页都会装载到内存中,计算机中的硬件和软件会将数据从 RAM 临时传输到磁盘中来弥补内存的不足。如果没有虚拟内存的话,一旦你将计算机内存填满后,计算机会对你说,您无法再加载任何应用程序,请关闭另一个应用程序以加载新的应用程序。对于虚拟内存,计算机可以执行操作是查看内存中最近未使用过的区域,然后将其复制到硬盘上。虚拟内存通过复制技术实现了 妹子,你快来看哥哥能装这么多程序 的资本。复制是自动进行的,你无法感知到它的存在。

虚拟内存的实现有以下两种方式:

  • 请求分页存储管理。
  • 请求分段存储管理

什么是虚拟内存:

虚拟内存是操作系统为每个进程提供的一种抽象,每个进程都有属于自己的、私有的、地址连续的虚拟内存,当然我们知道最终进程的数据及代码必然要放到物理内存上,那么必须有某种机制能记住虚拟地址空间中的某个数据被放到了哪个物理内存地址上,这就是所谓的地址空间映射,也就是虚拟内存地址与物理内存地址的映射关系,那么操作系统是如何记住这种映射关系的呢,答案就是页表,页表中记录了虚拟内存地址到物理内存地址的映射关系。有了页表就可以将虚拟地址转换为物理内存地址了,这种机制就是虚拟内存。每个进程都有自己的虚拟地址空间,进程内的所有线程共享进程的虚拟地址空间。

 

进程和线程的区别:

1、内存空间的区别:

 进程是有独立的内存空间,每个进程之间是相互独立的,互不干扰,

   线程有共享的内存空间(也有私有的)(一个程序内共享)

2、安全性:进程是相互独立的,一个进程的奔溃不会影响到其他的进程,进程是安全的,

线程存在内存空间的共享,一个线程的奔溃可能会影响到其他的线程的执行。,线程的安全性不如进程

关系:进程是相互独立的,一个进程下可以有一个或者多个线程

 总结:进程独立、线程可共享;进程安全,线程可能不安全;进程资源分配,线程资源调度。

3.进程切换与线程切换的区别:进程切换与线程切换的一个最主要区别就在于进程切换涉及到虚拟地址空间的切换而线程切换则不会。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。

 

 用户态和内核态的切换:

a. 系统调用

        这是用户态进程主动要求切换到内核态的一种方式,用户态进程通过系统调用申请使用操作系统提供的服务程序完成工作,比如前例中fork()实际上就是执行了一个创建新进程的系统调用。而系统调用的机制其核心还是使用了操作系统为用户特别开放的一个中断来实现,例如Linux的int 80h中断。

b. 异常

        当CPU在执行运行在用户态下的程序时,发生了某些事先不可知的异常,这时会触发由当前运行进程切换到处理此异常的内核相关程序中,也就转到了内核态,比如缺页异常。

c. 外围设备的中断

        当外围设备完成用户请求的操作后,会向CPU发出相应的中断信号,这时CPU会暂停执行下一条即将要执行的指令转而去执行与中断信号对应的处理程序,如果先前执行的指令是用户态下的程序,那么这个转换的过程自然也就发生了由用户态到内核态的切换。比如硬盘读写操作完成,系统会切换到硬盘读写的中断处理程序中执行后续操作等。

      这3种方式是系统在运行时由用户态转到内核态的最主要方式,其中系统调用可以认为是用户进程主动发起的,异常和外围设备中断则是被动的

说说分段和分页:
    页是信息的物理单位,分页是为实现离散分配方式,以消减内存的外零头,提高内存的利用率;或者说,分页仅仅是由于系统管理的需要,而不是用户的需要。
    段是信息的逻辑单位,它含有一组其意义相对完整的信息。分段的目的是为了能更好的满足用户的需要。页的大小固定且由系统确定,把逻辑地址划分为页号和页内 地址两部分,是由机器硬件实现的,因而一个系统只能有一种大小的页面。 段的长度却不固定,决定于用户所编写的程序,通常由编辑程序在对源程序进行编辑时,根据信息的性质来划分。
    分页的作业地址空间是维一的,即单一的线性空间,程序员只须利用一个记忆符,即可表示一地址。分段的作业地址空间是二维的,程序员在标识一个地址时,既需给出段名,又需给出段内地址

 

 

缓存:

CPU 运算器的运算速度远比内存读写速度快,所以CPU 大部分时间都在等数据从内存读取,运算完数据写回内存。

解决:因为CPU 运行速度实在太快,主存(就是内存)的数据读取速度和CPU 运算速度差了有几个数量级,因此现代计算机系统通过在CPU 和主存之前加了一层读写速度尽可能接近CPU 运行速度的高速缓存来做数据缓冲,这样缓存提前从主存获取数据,CPU 不再从主存取数据,而是从缓存取数据。这样就缓解由于主存速度太慢导致的CPU 饥饿的问题。同时CPU 内还有寄存器,一些计算的中间结果临时放在寄存器内。

RAID 的不同级别

RAID 称为 磁盘冗余阵列,简称 磁盘阵列。利用虚拟化技术把多个硬盘结合在一起,成为一个或多个磁盘阵列组,目的是提升性能或数据冗余。

 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被init进程(进程号为1)所收养,并由init进程对它们完成状态收集工作。

 僵尸进程:一个进程使用fork方法创建子进程,如果子进程退出,而父进程并没有调用wait或waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。

进程调度

调度种类

  • 高级调度:(High-Level Scheduling)又称为作业调度,它决定把后备作业调入内存运行;

  • 低级调度:(Low-Level Scheduling)又称为进程调度,它决定把就绪队列的某进程获得CPU;

  • 中级调度:(Intermediate-Level Scheduling)又称为在虚拟存储器中引入,在内、外存对换区进行进程对换。

cpu调度算法在进程之间切换CPU,目标需要周转时间小,响应时间快,完成的任务量大。

(FCFS)先来先服务、SJF短作业优先、RR轮转调度(时间片)、优先级调度

如何查看系统日志?

linux常见命令:etc目录是系统配置文件存放的目录,例如用户的账号密码配置、各种服务的初始配置等,一般来说这个目录下的各配置是可以让一般使用者查看的,但是只有root有权利修改; bin是binary的缩写,里面存放的是可执行的二进制文件;  Linux系统上的/proc目录是一种文件系统,即proc文件系统。与其它常见的文件系统不同的是,/proc是一种伪文件系统(也即虚拟文件系统),存储的是当前内核运行状态的一系列特殊文件,用户可以通过这些文件查看有关系统硬件及当前正在运行进程的信息,甚至可以通过更改其中某些文件来改变内核的运行状态。

 文件的后缀:

● 源码.tar、.tar.gz、.tgz、.zip、.tar.bz表示压缩文件,创建命令一般为tar,gzip,zip等。
● .sh表示shell脚本文件,通过shell语言开发的程序。
● .pl表示perl语言文件,通过perl语言开发的程序。
● .py表示python语言文件,通过python语言开发的程序。
● .html、.htm、.php、.jsp、.do表示网页语言的文件。
● .conf表示系统服务的配置文件。
● .rpm表示rpm安装包文件。

文件类别:

d  目录文件。
l  符号链接(指向另一个文件,类似于瘟下的快捷方式)。
s  套接字文件。
b  块设备文件,二进制文件。
c  字符设备文件。
p  命名管道文件。
-  普通文件,或更准确地说,不属于以上几种类型的文件。

ls -l后的参数分析:

544365   -rw-r–r--.   1 root root 3 Jan 28 20:55 a.txt

inode 索引节点编号:544365
文件类型 :文件类型是’-’,表示这是一个普通文件
文件权限:rw-r–r-- 表示文件可读、可写、可执行,文件所归属的用户组可读可执行,其他用户可读可执行
硬链接个数 表示a.txt这个文件没有其他的硬链接,因为连接数是1,就是他本身
文件属主 表示这个文件所属的用户,这里的意思是a.txt文件被root用户拥有,是第一个root
文件属组 表示这个文件所属的用户组,这里表示a.txt文件属于root用户组,是第二个root
文件大小 文件大小是3个字节
文件修改时间 这里的时间是该文件最后被更新(包括文件创建、内容更新、文件名更新等)的时间可用如下命令查看文件的修改、访问、创建时间

 
 

在shell中输入umask命令可以看到当前系统中文件权限掩码的默认值;umask-s以人性化符号展示掩码对应文件权限。

用chmod修改文件权限,chown用户属主修改,chgrp用户属组修改。(chmod  777  1.text)

netstat查看网络连接情况,查看8080端口被那个进程占用:netstat  -anp | grep 8080;(需要切换到root用户)

 
Proto显示连接使用的协议,RefCnt表示连接到本套接口上的进程号,Types显示套接口的类型,State显示套接口当前的状态,Path表示连接到套接口的其它进程使用的路径名。

一个是Active Internet connections,称为有源TCP连接,其中"Recv-Q"和"Send-Q"指%0A的是接收队列和发送队列。这些数字一般都应该是0。如果不是则表示软件包正在队列中堆积。这种情况只能在非常少的情况见到。

另一个是Active UNIX domain sockets,称为有源Unix域套接口(和网络套接字一样,但是只能用于本机通信,性能可以提高一倍)。

 

telnet测试连接有效性。

curl -l 显示网站的头信息

top命令查看进程信息,top快捷键

df查看磁盘使用情况(root用户)

who命令用于列举出当前已登录系统的用户名称。其输出为:用户名、tty号、时间日期、主机地址。

pwd查看当前目录

1.rm -rf * 删除当前目录下的所有文件,这个命令很危险,应避免使用。
所删除的文件,一般都不能恢复!
2.rm -f 其中的,f参数 (f --force ) 忽略不存在的文件,不显示任何信息
不会提示确认信息。

    -f, --force    忽略不存在的文件,从不给出提示。
    -r, -R, --recursive   指示rm将参数中列出的全部目录和子目录均递归地删除

日志关键字段次数统计

 

 42是行数,38是单词数,146是字节数

 tail命令查看默认文件后10行:

 

 

  • 要统计出现次数最多的IP可以利用以下shell脚本:

    • cat test.txt | awk '{print $2}' | sort | uniq -c | sort -n -r | head -n 1
  • 参数含义:

    • (针对首行不是IP地址信息的情况)tail -n +3 :去掉上面用红色标明的两行。

    • awk '{ print $5}':取数据的低5域(第5列),本例中是第2列,因此将5写为2。

    • (多加的限制,可忽略)cut -d : -f 1 :取蓝色部分前面的IP部分。

    • sort:对IP部分进行排序。

    • uniq -c:打印每一重复行出现的次数,并去掉重复行

    • sort -n -r:按照重复行出现的次序倒序排列。

    • head -n 5:取排在前5位的IP,本例中是找"最多",因此5可以写为1.

看日志中出现100次以上的IP

  • cat access_log |cut -d ' ' -f 1 | sort |uniq -c | awk '{if (1>100) print0}'|sort -nr | less

当前WEB服务器中联接次数最多的ip地址

 

  • netstat -ntu | tail -n +3|awk '{ print $5}' | cut -d : -f 1 | sort | uniq -c| sort -n -r | head -n 5

  • 参数解析:

    • tail -n +3 :去掉上面用红色标明的两行,从第三行开始显示。

    • awk '{ print $5}':取数据的低5域(第5列),上面蓝色标明。

    • cut -d : -f 1 :取蓝色部分前面的IP部分。

    • sort:对IP部分进行排序。

    • uniq -c:打印每一重复行出现的次数。(并去掉重复行)

    • sort -n -r:按照重复行出现的次序倒序排列。

    • head -n 5:取排在前5位的IP

 

eg:查看1.text文件从第5行开始找出第7列进行从小到大的排序并取前5个。

M可按内存降序排序,top快捷键P可按照占用CPU降序排序。

ifconfig查看ip地址。

   显示文件目录命令ls

      改变当前目录命令cd  如cd / /home
      建立子目录mkdir  mkdir  subfile
      删除子目录命令rmdir  如 rmdir /mnt/cdrom
      删除文件命令RM  如 rm /ucdos.bat
      文件复制命令cp   如 cp /ucdos/* /fox
      获取帮助信息命令man 如 man ls

      显示文件的内容more ,空格键上下翻页,q退出
      显示文件的内容less  如 less mwm.lx,上下键可以继续查看(以行的形式逐行显示)

linux如何将文件从一台服务器转移到另一台服务器?

      ping测试网络连接 ping 192.168.249.13

  • scp是secure copy的简写,是linux系统下基于ssh登陆进行安全的远程文件拷贝命令。

    • 和它类似的命令有cp,不过cp只是在本机进行拷贝不能跨服务器,
  • scp传输是加密的。可能会稍微影响一下速度。

  • 当你服务器硬盘变为只读 read only system时,用scp可以帮你把文件移出来。

  • 另外,scp还非常不占资源,不会提高多少系统负荷,在这一点上,rsync就远远不及它了。

 

 

查看CPU信息 cat /proc/cpuinfo(硬件信息)  top   dstat 查看CPU使用状态信息

查看内存信息cat /proc/meminfo;

查看linux系统版本信息:uname-a        cat    /etc/issue(ubuntu版本)      cat   /proc/version

man命令,该命令可以显示指定命令的用法和描述

touch命令可以在Linux系统中创建大小为0的任意类型文件,cat命令查看;less命令也可以让你浏览文件,less命令非常快,并且支持上下键查看文件的开头和末尾。然而more命令和它类似,只是more命令只能用enter键实现文件的向前翻页,不支持回退。

grep命令:文本过滤器,需要与表达式搭配。可以在文件中搜索指定格式的字符串,同时对其进行标准输出。

^$显示空行行数     ^[^$]显示非空行行数

查看日志中的关键字: grep word file

find命令用来检索文件,可以用“-name”选项来检索指定名称的文件

diff命令用来找出2个文件的不同点。

uniq命令用来过滤文件中的重复行;

两个文件合并:

1. 一个文件在上,一个文件在下

  cat file1 file2 > file3

2. 一个文件在左,一个文件在右(横向合并)

  paste file1 file2 > file3

paste -s 文件平铺

一个文件去掉重复的行

1. 重复的多行记为一行

  sort file |uniq

2. 重复的行全部去掉

  sort file |uniq -u

 vi编辑器:

  • 1、用vi打开文件后,是处于「命令行模式(command mode)」,您要切换到「插入模式(Insert mode)」才能够输入文字。切换方法:在「命令行模式(command mode)」下按一下字母「i」就可以进入「插入模式(Insert mode)」,这时候你就可以开始输入文字了。
  • 2、编辑好后,需从插入模式切换为命令行模式才能对文件进行保存,切换方法:按「ESC」键。
  • 3、保存并退出文件:在命令模式下输入:wq即可!(别忘了wq前面的)

 在「命令行模式(command mode)」下,按一下「:」冒号键进入「Last line mode」,

例如:  : w filename (输入 「w filename」将文章以指定的文件名filename保存)  : wq (输入「wq」,存盘并退出vi)     : q! (输入q!, 不存盘强制退出vi)

sed命令用法:

sed是一种流编辑器,它是文本处理中非常有用的工具,能够完美的配合正则表达式使用,功能不同凡响。处理时,把当前处理的行存储在临时缓冲区中,称为『模式空间』(pattern space),接着用sed命令处理缓冲区中的内容,处理完成后,把缓冲区的内容送往屏幕。接着处理下一行,这样不断重复,直到文件末尾。文件内容并没有改变,除非你使用重定向存储输出。sed主要用来自动编辑一个或多个文件,简化对文件的反复操作,编写转换程序等。

sed    's/This/aaa/'   test.txt          将test.txt文件中的This替换成aaa

sed -n 's/This/aaa/p' test.txt           -n /p只打印发生替换的行

# sed '2d' test.txt       删除第二行

# sed '2,$d' test.txt       删除第二行到末尾的所有行

sed    '/my/r   test1.txt'    test.txt      /r从文件读入,将test1.txt中读到test.txt的my句后(每一个my后)

变形:y命令

把1-10行内所有的abcde转变为大写,注意,正则表达式元字符不能使用这个命令:[root@vagrant-centos65 workspace]# sed '1,10y/abcde/ABCDE/' test.txt

退出:q命令

打印完第3行,退出sed  [root@vagrant-centos65 workspace]# sed '3q' test.txt

find      -mmin   -5         # 查找在系统中最后5分钟里修改过的文件
find      -mtime   -1       #查找在系统中最后24小时里修改过的文件

-mtime    -n +n                   按文件更改时间来查找文件,-n指n天以内,+n指n天以前
-atime     -n +n                    #按文件访问时间来查找文件
-ctime     -n +n                   #按文件创建时间来查找文件,-n指n天以内,+n指n天以前

-perm    +-mode               按执行权限来查找
-user     username             按文件属主来查找
-group   groupname             按组来查找

查找最近30分钟修改的当前目录下的.php文件

 find  -name  '*.php'  -mmin -30

查找最近24小时修改的当前目录下的.php文件

find   -name  '*.php'  -mtime 0

$find     -size   100c         -print       # 查长度为100c的文件

find   -name ap* -o -name may*   查找以ap或may开头的文件

find   ~/某目录   -empty                     查找大小为0的文件或空目录
find   ~/某目录  -size   +512k                查大于512k的文件
find   ~/某目录   -size   -512k               查小于512k的文件

find      -links   +2                查硬连接数大于2的文件或目录
find    -perm    700                查权限为700的文件或目录 

 

软链接ln -s和硬链接ln:硬链接就是一个 inode 号对应多个文件。就是同一个文件使用了多个别名(上图中 hard link 就是 file 的一个别名,他们有共同的 inode);软链接是若文件用户数据块中存放的内容是另一文件的路径名的指向,则该文件就是软连接。

区别: 软链接文件的大小和创建时间和源文件不同。软链接文件只是维持了从软链接到源文件的指向关系,不是源文件的内容,大小不一样容易理解;硬链接文件和源文件的大小和创建时间一样。

Java中如何获取到线程dump文件

死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步:

(1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java

(2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid

 

ps ax : 显示当前系统进程的列表 ;ps命令能够查看刚刚系统的进程信息

ps aux : 显示当前系统进程详细列表以及进程用户

pid    %cpu    %mem  VSZ   RSS    TTY  STAT  START  TIME COMMAND

VSZ–进程的虚拟大小;  RSS–包括所有分配的栈内存和堆内存,可以理解为内存中页的数量;    TTY–控制终端的ID;STAT–也就是当前进程的状态,其中S-睡眠,s-表示该进程是会话的先导进程,N-表示进程拥有比普通优先级更低的优先级,R-正在运行,D-短期等待,Z-僵死进程,T-被跟踪或者被停止等等;
STRAT–这个很简单,就是该进程启动的时间;   TIME–进程已经消耗的CPU时间,注意是消耗CPU的时间 ;  COMMOND–命令的名称和参数
top命令反应的是系统进程动态信息,默认10s更新一次
[root@Linux ~]#top
top – 22:30:58 up 35 min,  1 user,  load average: 0.12, 0.07, 0.08
Tasks:  72 total,   1 running,  71 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.2%us,  0.2%sy,  0.0%ni, 99.7%id,  0.0%wa,  0.0%hi,  0.0%si,  0.0%st
Mem:    373320k total,    88600k used,   284720k free,     9956k buffers
Swap:   514072k total,        0k used,   514072k free,    45092k cached
PID  USER    PR   NI  VIRT  RES  SHR   S   %CPU  %MEM    TIME+  COMMAND
3217  root      15   0  2160    976  788       R    1        0.3   0:00.07          top
1        root      15   0  2032    644  552       S    0        0.2   0:01.55          init
2        root      RT   0     0       0    0            S    0        0.0   0:00.02     migration/0
3        root      34  19     0       0    0            S    0        0.0   0:00.00     ksoftirqd/0
4        root      RT   0     0       0    0            S    0        0.0   0:00.00     watchdog/0
5        root      RT   0     0       0    0            S    0        0.0   0:00.02     migration/1
6        root      39  19     0       0    0            S    0        0.0   0:00.00     ksoftirqd/1
7        root      RT   0     0       0    0            S    0        0.0   0:00.00  watchdog/1
……
以上就是top命令的部分输出,可以看到该命令显示了较为详细的信息
顶部始终显示的是系统当前最活跃的进程,此时还可以接受用户的键盘输入向进程发送信号等待

Linux上查找线程使用的CPU时间最长

1. 找出cpu耗用厉害的进程pid    终端执行top命令,然后按下shift+p 查找出cpu利用最厉害的pid号:pid号:31365     

2. 根据上面第一步拿到的pid号,top -H -p pid 。然后按下shift+p,查找出cpu利用率最厉害的线程号    线程号:31441   

3. 将获取到的线程号转换成16进制,去百度转换一下就行    转换的结果:7AD1   

4. 使用jstack工具将进程信息打印输出    jstack pid号 > /tmp/t.dat    jstack 31365 > /tmp/t.dat5. 编辑/tmp/t.dat文件,查找线程号对应的信息    这个就是java进程中耗用CPU频繁和严重的线程信息,可以将该信息提供给开发人员,他们会根据线程信息分析出是属于哪一块的业务内容。到这里就可以交给开发人员了,相信这样的话,处理问题的时间可以大大减少。

 

守护线程和用户线程是什么?

用户线程一般用户执行的用户级的线程

守护线程:也叫做后台线程,脱离于终端,用来服务于用户线程 例如:GC是一个单独的线程来处理,GC线程就是一个守护线程

 

14.测试

测试与测开:一般需要写测试工具,自动化测试代码,也需要做普通的功能测试或是白盒测试。对工程师要求也和其他两种工程师不一样,不能具有开发的基于程序的思维模式,但有要开发的编码能力;不像开发那样深入地掌握一种编码语言,但对于脚本语言要掌握的比较多,如java,python,php,shell,有时也需要会c,c++,ruby等编程语言。而且更加要有测试工程是敏锐的思维,能从用户体验角度来解决问题,编写测试工具等。

软件测试工程师一般就是从用户角度出发,检测开发工程师做的东西是不是符合产品的需求,或是用户体检好不好。功能测试不要求有太专业的知识,但是要细心,对产品敏感。所以有很多不是计算机专业的人员照样可以做功能测试工程师,另外也有比较专业的白盒或是灰盒测试,这就要求测试人员会些儿编程技术了,但是要求不太高,不必会某种语言的高级编程,普通应用或是代码段能看懂就行。问题要考虑全面,细致,有原则,不能跟着开发和产品走,这是测试人员的要求。

GitLab bug等级:

1.Blocker:阻碍测试工作无法进行的BUG

2.Critical:严重功能未实现或缺陷
3.FEATURE:新功能

4.Major:功能实现与设计不符或不完善,对整体功能影响较大,影响用户体验

5.Minor:一般的UI问题

6.Normal:一般的功能缺陷,功能实现不完善,严重的UI类问题

 

selenium流程、获取元素方法?

先import,然后webdriver模拟打开一个浏览器(初始化,下载浏览器驱动),获取特定的网址,然后通过获取元素模拟鼠标点击页面上的按钮(或者其他动作,比如在特定的文本框输入特定的内容),(最后quit()一下)

获取元素方法:xpath,css选择器,文本内容…..等等,比如find_element_by_xpath、find_element_by_css_selector、find_element_by_link_text

测试一个前端页面,button按钮不好使,原因,不获取源码的前提下,如何解决(提示接口测试)?

因为这是个前端界面,可以按F12打开开发者工具,在network里按钮点击时请求有没有发出去,看状态码,有没有生成新文件之类的,确定是不是连接的问题。

postman模拟发包过去测试也行。

页面加载页面慢原因?

1.自身网速问题 带宽不足             2.硬件配置低               3.内存被战满      4.dns解析慢              5.接受数据时间过长

6.加载某个资源太慢       7.后端代码问题          8.前端页面请求的资源过多        9、网页本身中包含了追踪或者是分析用户的工具 10.并发问题

 性能测试方案设计?

1.用户需求数

   1)调查系统当前和未来使用的用户数

  系统用户数=本系统目前注册的用户数,注册用户数并不代表他会每天并且无时无刻的使用着。

  在线用户数=同时在线对系统进行操作的用户数量(相当于混合场景)

  并发用户数=同时在线并且同时操作同一个功能(单场景添加集合点)

  估算未来一到五年使用此用户的数量,可以根据一些日志数据估算出来的。

  2)调查系统当前和未来的每日、月活跃用户数

  当前活跃用户数,即某天大概有多少用户使用本系统:那么这部分数据一说来也就是当前真正对系统构成压力的数量。

2.业务数据量

       1)调查当前和未来背景数据量

  因为从100条数据中查10条也许很快,但是未来数据量变成100w那你懂得...

  2)调查当前和未来业务每天使用的总笔数

  每个用户每天可能操作数,平均需要多少次来执行这个操作?那么根据用户数,我们就可以确定每天下单的笔数。如50人,平均每人每天下10次,每次下100笔,那么总笔数就是50*10*100=50000笔。注意此数据根据TPS换算后,我们可以换算出系统的业务总处理量是否能达到这个数据,这也是一个很重要的指标。

  3)调查当前和未来高峰时业务的总笔数

  即上面所描述的特殊情况,这也是必须要考虑,并且拿到的数据。

3.场景业务的调查

        1)系统关键、核心的业务

  从系统亮点出发,以主要的业务逻辑点为第一核心:这些功能对系统或公司来说往往具有举足轻重的地位,无论怎样都必须要优先执行满足这以功能的性能测试

  2)高访问量的功能,经常承受压力的功能点

  系统中表现在系统关键、核心业务前面必须要经过的地方:比如对于百度搜索来说,其核心业务是搜索功能,但是首先要面对的其高访问量对是搜索输入框加载的首页,百度首页加载即高访问量的请求

  3)业务复杂度高

  往往说来业务逻辑复杂度的都具备1、2点的要素,可能其功能使用的人数较少但是对系统有很严重影响:这些功能由于其业务逻辑具有的复杂度,往往出错的可能性也比较高,所以这些功能也是必须要进行测试的。

4.与性能指标指标相关的调查

        1、调查每秒事务数(TPS)

  这是衡量系统处理能力的一个重要指标,同时这个指标在一定程序也关系到业务数量是否能够及时完成,所以需要获得。

  2、调查90%(或95%)响应时间

  只看平均时间是不太科学的,对于我们的系统来说需要保证绝大多数的用户其响应时间都是非常快的,所以我们从90%或95%用户响应时间为指标的标准。

  3、平均响应时间和TPS的波动率

  这是对响应时间的补充,要求其系统响应时间应尽量稳定,TPS的波动率受测试方法和思考、间隔时间的影响。

 

软件测试是使用人工或自动的手段来运行或测定某个软件系统的过程,其目的在于检验它是否满足规定的需求或弄清预期结果与实际结果之间的差别。目的包括:发现软件程序中的错误、是否符合设计要求,是否符合技术要求,进行有关验证以及评估软件的质量。提交软件交付速率和产品质量

软件质量保证:质量保证QA关注在软件产品生成的整个过程,主要验证软件产品开发过程中相关实施过程的完整性、一致性和有效性,确保开发活动和测试活动等遵循正确的过程,为软件产品达到合适的质量级别提供信心。

冒烟测试:就是对该系统整体重点功能点的功能流程测试,要确保通过冒烟测试,系统能够跑通(正常运行);不要求每一个功能都面面俱到,但是要保证所有被修改过以及与修改相关的功能、主要的功能都是可用的,即证明这个版本可进行系统测试。

准备
测试经理和项目经理等相关人员从测试用例库中选定重要的测试用例,标记为冒烟测试用例。或者单独编写。
1、主流程和主功能的确认
  要求测试人员在测试开始前跟开发人员确认需求和重要的流程、功能,最好将功能点和流程以及预期结果和开发人员说明清楚。冒烟测试不要求测试结果像正式测试阶段那么准确,但是也需要列一个指标来衡量测试是否通过。)
2、预计时间
 根据列出的功能点和开发人员代码质量的可信度,评估冒烟测试在不同环境下可能花费的最长和最短时间,列到测试计划中。
3、数据的准备
在测试前,透彻的了解主要功能对应表的存储结构,准备好需要的数据。冒烟测试开始后,不会因为这些工作浪费时间。
执行
测试人员严格按照前期的约定去校验主流程,全部校验完和开发人员报告情况,不能放过一个主要的测试功能点。
总结
通常测试组在执行完冒烟测试,会回复本次冒烟测试结果,接受本次版本或者拒绝本次版本。如果拒绝本次版本,需要附录失败的原因和详细缺陷的描述。

软件测试的流程是什么?

需求调查: 全面了解系统概况、应用领域、软件开发周期、软件开发环境、开发组织、时间安排、功能需求、性能需求、质量需求及测试要求等

制定初步的项目计划: 在充分共同和协商的基础上制定我们的测试计划。

测试准备: 组织测试团队、培训、建立测试和管理环境等。

测试设计: 按照测试要求进行每个测试项的测试设计,包括测试用例的设计及测试脚本的开发等。

测试实施: 按照测试计划进行实施测试。

测试评估: 根据测试的结果,出具测试评估报告。

测试最规范的过程如下:需求测试->概要设计测试->详细设计测试->单元测试->集成测试->系统测试->验收测试
需求分析、需求评审->编写测试计划->编写测试用例、用例评审->执行测试、提交bug、回归测试->编写测试总结报告

测试的原则

①、尽早的、不断地进行测试

②、测试用例由输入数据和与之对应的输出结果组成,应包括合理和不合理的输入条件

③、开发者应尽量避免检查自己的程序

④、设计测试用例时,应包括合理和不合理的输入条件

⑤、充分注意测试中的集群现象,严格执行测试计划,排除测试的随意性

⑥、对每一个测试结果做全面检查

⑦、妥善保存测试计划,方案,用例,BUG记录及最终分析报告等文档

 白盒:

分支覆盖法:设计足够多的测试用例,使得被测试程序中的每个判断的“真”、“假”分支至少被执行一次。在本例中共有两个判断if(x>0 && y>0)(记为P1)和if(magic < 0)(记为P2)。

 

按照研发阶段一般分为4个部分:单元测试、集成测试、系统测试、验收测试

单元测试:完成最小的软件设计单元(模块)的验证工作,目标是确保模块被正确的编码,使用过程设计描述作为指南,对重要的控制路径进行测试以发现模块内的错误,通常情况下是白盒的,对代码风格和规则、程序设计和结构、业务逻辑等进行静态测试,及早的发现和解决不易显现的错误。

集成测试:通过测试发现与模块接口有关的问题。

 

自顶向下集成:模块集成的顺序是首先集成主模块,然后按照控制层次结构向下进行集成,隶属于主模块的模块按照深度优先或广度优先的方式集成到整个结构中去。

自底向上集成:从原子模块开始来进行构造和测试,因为模块是自底向上集成的,进行时要求所有隶属于某个给顶层次的模块总是存在的,也不再有使用稳定测试桩的必要。

 

系统测试:是基于系统整体需求说明书的黑盒类测试,应覆盖系统所有联合的部件。系统测试是针对整个产品系统进行的测试,目的是验证系统是否满足了需求规格的定义,找出与需求规格不相符合或与之矛盾的地方。系统测试的对象不仅仅包括需要测试的产品系统的软件,还要包含软件所依赖的硬件、外设甚至包括某些数据、某些支持软件及其接口等。因此,必须将系统中的软件与各种依赖的资源结合起来,在系统实际运行环境下来进行测试。

回归测试:回归测试是指在发生修改之后重新测试先前的测试用例以保证修改的正确性。理论上,软件产生新版本,都需要进行回归测试,验证以前发现和修复的错误是否在新软件版本上再次出现。根据修复好了的缺陷再重新进行测试。回归测试的目的在于验证以前出现过但已经修复好的缺陷不再重新出现。一般指对某已知修正的缺陷再次围绕它原来出现时的步骤重新测试。

验收测试:是指系统开发生命周期方法论的一个阶段,这时相关的用户或独立测试人员根据测试计划和结果对系统进行测试和接收。它让系统用户决定是否接收系统。它是一项确定产品是否能够满足合同或用户所规定需求的测试。验收测试包括Alpha测试和Beta测试。用户验收测试内容?

 

  a.配置审查。确保已开发软件的所有文件资料均已编写齐全,并分类编目
  b.Alpha测试。是由用户在开发者的场所来进行的,在一个受控的环境中进行。

 

  c.Beta测试。由软件的最终用户在一个或多个用户场所来进行的,开发者通常不在现场,用户记录测试中遇到的问题并报告给开发者,开发者对系统进行最后的修改,并开始准备发布最终的软件
 

请你回答一下单元测试、集成测试、系统测试、验收测试、回归测试这几步中最重要的是哪一步?

这些测试步骤分别在软件开发的不同阶段对软件进行测试,我认为对软件完整功能进行测试的系统测试很重要,因为此时单元测试和集成测试已完成,能够对软件所有功能进行功能测试,能够覆盖系统所有联合的部件,是针对整个产品系统进行的测试,能够验证系统是否满足了需求规格的定义,因此我认为系统测试很重要。

应用场景:

集成测试:完成单元测试后,各模块联调测试;集中在各模块的接口是否一致、各模块间的数据流和控制流是否按照设计实现其功能、以及结果的正确性验证等等;可以是整个产品的集成测试,也可以是大模块的集成测试;集成测试主要是针对程序内部结构进行测试,特别是对程序之间的接口进行测试。集成测试对测试人员的编写脚本能力要求比较高。测试方法一般选用黑盒测试和白盒测试相结合。

系统测试:针对整个产品的全面测试,既包含各模块的验证性测试(验证前两个阶段测试的正确性)和功能性(产品提交个用户的功能)测试,又包括对整个产品的健壮性、安全性、可维护性及各种性能参数的测试。系统测试测试软件《需求规格说明书》中提到的功能是否有遗漏,是否正确的实现。做系统测试要严格按照《需求规格说明书》,以它为标准。测试方法一般都使用黑盒测试法。

程序插桩: 一种基本的动态测试方法,向源程序中添加一些语句实现对程序代码的执行、变量的变化等情况的检查,可以获得程序的控制流和数据流信息。如果我们想要了解一个程序在某次运行中可执行语句被覆盖的情况,或是每个语句的实际执行次数,最好的办法就是利用插装技术,它在软件测试技术上占有非常高的地位。最简单的插装:在程序中插入打印语句printf(“ ...”)语句。

性能:响应速度、CPU占用,联网的话考虑多用户并发/弱网环境,app的话考虑耗电量

WEB测试和App测试从流程上来说,没有区别。都需要经历测试计划方案,用例设计,测试执行,缺陷管理,测试报告等相关活动。从技术上来说,WEB测试和APP测试其测试类型也基本相似,都需要进行功能测试、性能测试、安全性测试、GUI测试等测试类型。

他们的主要区别在于具体测试的细节和方法有区别,比如:性能测试,在WEB测试只需要测试响应时间这个要素,在App测试中还需要考虑流量测试和耗电量测试。

兼容性测试:在WEB端是兼容浏览器,在App端兼容的是手机设备。而且相对应的兼容性测试工具也不相同,WEB因为是测试兼容浏览器,所以需要使用不同的浏览器进行兼容性测试(常见的是兼容IE6,IE8,chrome,firefox)如果是手机端,那么就需要兼容不同品牌,不同分辨率,不同android版本甚至不同操作系统的兼容。(常见的兼容方式是兼容市场占用率前N位的手机即可),有时候也可以使用到兼容性测试工具。

app的兼容:

系统(ios android)、厂商(华为、vivo)、屏幕分辨率(尺寸)、网络(5g、4g)   第三方软件冲突(输入法)

安装测试:WEB测试基本上没有客户端层面的安装测试,但是App测试是存在客户端层面的安装测试,那么就具备相关的测试点。

还有,App测试基于手机设备,还有一些手机设备的专项测试。如交叉事件测试,操作类型测试,网络测试(弱网测试,网络切换),耗电量

交叉事件测试:就是在操作某个软件的时候,来电话、来短信,电量不足提示等外部事件。

操作类型测试:如横屏测试,手势测试

网络测试:包含弱网和网络切换测试。需要测试弱网所造成的用户体验,重点要考虑回退和刷新是否会造成二次提交。弱网络的模拟,据说可以用360wifi实现设置。

从系统架构的层面,WEB测试只要更新了服务器端,客户端就会同步会更新。而且客户端是可以保证每一个用户的客户端完全一致的。但是APP端是不能够保证完全一致的,除非用户更新客户端。如果是APP下修改了服务器端,意味着客户端用户所使用的核心版本都需要进行回归测试一遍。

还有升级测试:升级测试的提醒机制,升级取消是否会影响原有功能的使用,升级后用户数据是否被清除了。

如何描述bug?

1. Id(BUG编号):一般自动生成
2.      测试环境:机型、系统版本、分辨率、浏览器
3.      提交信息:BUG提交人,Bug接收人、接收部门,提交日期
4.      严重程度:系统崩溃、严重问题、次要问题、一般问题、文字错误问题、易用性问题、建议
5.      优先级:特急、加急、急、一般
6.      出现频率:经常出现、偶然出现、有时出现
7.      Bug之项目属性:项目名称,项目版本,所属模块
8.      BUG描述:摘要描述:简述BUG情况

bug的周期,以及描述一下不同类别的bug

1、New:(新的)

当某个“bug”被第一次发现的时候,测试人员需要与项目负责人沟通以确认发现的的确是一个bug,如果被确认是一个bug,就将其记录下来,并将bug的状态设为New

2、Assigned(已指派的)

当一个bug被指认为New之后,将其反馈给开发人员,开发人员将确认这是否是一个bug,如果是,开发组的负责人就将这个bug指定给某位开发人员处理,并将bug的状态设定为“Assigned”

3、Open(打开的)

一旦开发人员开始处理bug的时候,他(她)就将这个bug的状态设置为“Open”,这表示开发人员正在处理这个“bug”

4、Fixed(已修复的)

当开发人员进行处理(并认为已经解决)之后,他就可以将这个bug的状态设置为“Fixed”并将其提交给开发组的负责人,然后开发组的负责人将这个bug返还给测试组

5、Pending Reset(待在测试的)

当bug被返还到测试组后,我们将bug的状态设置为Pending Reset”

6、Reset(再测试)

测试组的负责人将bug指定给某位测试人员进行再测试,并将bug的状态设置为“Reset”

7、Closed(已关闭的)

如果测试人员经过再次测试之后确认bug 已经被解决之后,就将bug的状态设置为“Closed”

8、Reopen(再次打开的)

如果经过再次测试发现bug(指bug本身而不是包括因修复而引发的新bug)仍然存在的话,测试人员将bug再次传递给开发组,并将bug的状态设置为“Reopen”

9、Pending Reject(拒绝中)

如果测试人员传递到开发组的bug被开发人员认为是正常行为而不是bug时,这种情况下开发人员可以拒绝,并将bug的状态设置为“Pending Reject”

10、Rejected(被拒绝的)

测试组的负责人接到上述bug的时候,如果他(她)发现这是产品说明书中定义的正常行为或者经过与开发人员的讨论之后认为这并不能算作bug的时候,开发组负责人就将这个bug的状态设置为“Rejected”

11、Postponed(延期)

有些时候,对于一些特殊的bug的测试需要搁置一段时间,事实上有很多原因可能导致这种情况的发生,比如无效的测试数据,一些特殊的无效的功能等等,在这种情况下,bug的状态就被设置为“Postponed“

不同类别的bug:

Bug类型

•    代码错误

•    界面优化

•    设计缺陷

•    配置相关

•    安装部署

•    安全相关

•    性能问题

•    标准规范

•    测试脚本

•    其他

 

如何衡量产品质量?

1、是否满足用户需求;2、是否有好的用户体验

一、用户需求方面:1、该产品的目标群体是谁?他们的核心痛点是什么?        2、产品提供了哪几个主要的功能?用户的使用场景有哪些,是怎样解决用户需求的?            3、产品还有哪些次要功能,如何将产品串联起来,他们对主要功能的支撑体现在哪里?

二、用户体验方面 1、用户体验是一个完整的过程;              2、用户体验的基础是用户需求;      3、用户体验的目标是让产品做到“流畅”、“自然”,并且能减少用户操作过程中的焦虑感和孤独感。

什么是接口测试和为什么要做接口测试?

接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换,传递和控制管理过程,以及系统间的相互逻辑依赖关系等。

现在很多系统前后端架构是分离的,从安全层面来说,只依赖前端进行限制已经完全不能满足系统的安全要求,需要后端同样进行控制,在这种情况下就需要从接口层面进行验证。

如今系统越来越复杂,传统的靠前端测试已经大大降低了效率,而且现在我们都推崇测试前移,希望测试能更早的介入测试,那接口测试就是一种及早介入的方式。例如传统测试,你是不是得等前后端都完成你才能进行测试,才能进行自动化代码编写。而如果是接口测试,只需要前后端定义好接口,那这时自动化就可以介入编写接口自动化测试代码,手工测试只需要后端代码完成就可以介入测试后端逻辑而不用等待前端工作完成。

接口测试的策略:属于功能测试,所以跟我们以往的功能测试流程并没有太大区别,测试流程依旧是:1.测试接口文档(需求文档) 2.根据接口文档编写测试用例(用例编写完全可以按照以往规则来编写,例如等价类划分,边界值等设计方法)3. 执行测试,查看不同的参数请求,接口的返回的数据是否达到预期。

 按照软件内部结构划分:

 (1)黑盒测试(black-box testing):只关心输入和输出的结果(主要测试软件的功能和需求说明是否相符)—— 黑盒测试通常用于功能测试,不可视化。像用户一样看待产品就好了。优缺点:不考虑内部的实现;更贴近用户的角度。覆盖率较低;复用率较低;维护成本较高。
       关注:是否有不正确或遗漏的功能?输入和输出是否满足预期要求;是否有数据结构错误或外部信息访问错误;性能上是否满足要求?
(2)白盒测试(white-box testing):去研究软件里面的源代码和程序结构(主要是测试代码的)—— 白盒测试通常用于单元测试,可视化
    缺点:1昂贵 2无法坚持代码中遗漏的路径和数据敏感性错误 3不能直接验证需求的正确性。优点:检测内部代码的实现;更容易检测代码内部的逻辑。
(3)灰盒测试(gray-box testing):介于黑白盒两者之间的一种测试,在白盒测试交叉使用黑盒测试方法,在黑盒测试交叉使用白盒测试方法—— 灰盒测试通常用于网页测试

 

常用白盒测试方法:

静态测试:不用运行程序的测试,包括代码检查、静态结构分析、代码质量度量、文档测试等等,它可以由人工进行,充分发挥人的逻辑思维优势,也可以借助软件工具(Fxcop)自动进行。

动态测试:需要执行代码,通过运行程序找到问题,包括功能确认与接口测试、覆盖率分析、性能分析、内存分析等。

白盒测试中的逻辑覆盖包括语句覆盖、判定覆盖、条件覆盖、判定/条件覆盖、条件组合覆盖和路径覆盖。六种覆盖标准发现错误的能力呈由弱到强的变化

白盒测试需要遵循的原则有:1. 保证一个模块中的所有独立路径至少被测试一次;2. 所有逻辑值均需要测试真(true)和假(false);两种情况;3. 检查程序的内部数据结构,保证其结构的有效性;4. 在上下边界及可操作范围内运行所有循环。

 

 

按测试对象划分:性能测试、安全测试、兼容性测试、文档测试、易用性测试、业务测试、界面测试、安装测试、内存泄漏测试

常用的用例设计方法(黑盒测试方法)包括等价类、边界值、判定表、错误推断法、因果图、正交试验、状态迁移、流程分析等。

错误推测方法
一.    方法简介
1.         定义:基于经验和直觉推测程序中所有可能存在的各种错误, 从而有针对性的设计测试用例的方法。
2.         错误推测方法的基本思想:
列举出程序中所有可能有的错误和容易发生错误的特殊情况,根据他们选择测试用例。
1)        例如, 输入数据和输出数据为0的情况;输入表格为空格或输入表格只有一行。 这些都是容易发生错误的情况。可选择这些情况下的例子作为测试用例。
2)        例如,前面例子中成绩报告的程序,采用错误推测法还可补充设计一些测试用例:
I.          程序是否把空格作为回答
II.       在回答记录中混有标准答案记录
III.     除了标题记录外,还有一些的记录最后一个字符即不是2也不是3
IV.     有两个学生的学号相同
V.        试题数是负数。
3)    再如,测试一个对线性表(比如数组)进行排序的程序,可推测列出以下几项需要特别测试的情况:
I.          输入的线性表为空表;
II.       表中只含有一个元素;
III.     输入表中所有元素已排好序;
IV.     输入表已按逆序排好;
V.        输入表中部分或全部元素相同。

编写测试用例的模块:用例编号、测试项目、测试标题、重要级别、预置条件、测试输入、测试步骤、预期结果、实际结果、备注。

性能测试:测试目标是评估系统性能是否符合需要及设计目标;随着压力的持续增加,系统处理能力增加变缓,直到达到一个最大值,这是系统的最大负载点,这一段被称作负载测试。测试目标是评估当系统因为突发事件超出日常访问压力的情况下,保证系统正常运行情况下能够承受的最大访问负载压力;超过这个点后,再增加压力,系统的处理能力反而下降,而资源消耗缺更多,知道资源消耗达到极限,这个点可以看做是系统的崩溃点,超过这个点极限加大并发请求数目,系统不能再处理任何请求,这一段被称作压力测试,测试目标是评估可能导致系统崩溃的最大访问负载压力。

测试用例的边界?

 

边界值分析法就是对输入或输出的边界值进行测试的一种黑盒测试方法。通常边界值分析法是作为对等价类划分法的补充,这种情况下,其测试用例来自等价类的边界。

常见的边界值

1)对16-bit 的整数而言 32767 和 -32768 是边界

2)屏幕上光标在最左上、最右下位置

3)报表的第一行和最后一行

4)数组元素的第一个和最后一个

5)循环的第 0 次、第 1 次和倒数第 2 次、最后一次

场景法(重点方法)

    定义:通过运用场景来对系统的功能点或业务流程的描述,从而提高测试效果的一种方法。用例场景来测试需求是指模拟特定场景边界发生的事情,通过事件来触发某个动作的发生,观察事件的最终结果,从而用来发现需求中存在的问题。

    基本流:是经过用例的最简单的路径(无任何差错,程序从开始直接执行到结束)

    备选流:一个备选流可能从基本流开始,在某个特定条件下执行,然后重新加入基本流中,也可以起源于另一个备选流,或终止用例,不在加入到基本流中;(各种错误情况)

    场景法的运用:

           例:有一个在线购物的实例,用户进入一个在线购物网站进行购物,选购物品后,进行在线购买,这时需要使用帐号登录,登录成功后,进行付钱交易,交易成功后,生成订购单,完成整个购物过程。

                · 基本流

                · 备选流: 1) 进入购物网站,选择物品,登录账号,付费,生成订单

                                2)账号不存在

                                3)  账户

功能点--解锁

用例标号用例说明前置操作操作预期结果备注
1 查看地图、定位 进入网页首页 定位 显示定位地址正确  
2 查看车辆信息 首页地图显示有车辆图标 选择某一车辆图标 底部显示车辆信息,编号、价格、文字描述  
3 解锁 没有正在进行的订单也没有未支付的订单并且已支付押金 点击解锁 进入数码开锁页面  
4 解锁 没有正在进行的订单也没有未支付的订单并且未支付押金 点击解锁 进入绑定手机,支付押金流程  
5 解锁 有未支付订单 点击解锁 无法解锁,有提示信息,需支付未支付订单才能解锁  
6 解锁 有正在进行中的订单 点击解锁 无法解锁,有提示信息,需结束并支付正在进行的订单才能解锁  
7 绑定手机/手机认证 未绑定手机 输入正确手机号,正确的验证码 绑定成功 手机号有唯一性,11位,数字限制,
8 绑定手机/手机认证 未绑定手机 已绑定的手机号 提示该号码已被绑定  
9 绑定手机/手机认证 未绑定手机 非11位字符 提示输入正确手机号  
10 绑定手机/手机认证 未绑定手机 11位含非数字字符 提示输入正确手机号  
11 绑定手机/手机认证 未绑定手机 正确的手机号,错误的验证码 提示输入正确的验证码  
12 押金缴纳 已绑定手机 点击支付订单 进入支付页面  
13 输码解锁 已支付押金的用户解锁 输入正确单车编号且单车正常 显示单车计价标准  
14 输码解锁 已支付押金的用户解锁 输入错误的单车编号 提示“设备编号输入错误,无此号码请重新输入”信息  
15 输码解锁 已支付押金的用户解锁 输入正确单车编号且单车故障 提示“很抱歉,此单车正在维护中请换一辆”  
16 开锁中 输入正确单车编号,单车正常 点击立即使用 跳转至使用中  
17 开锁成功 开锁中   跳转开锁成功页  
18 开锁失败 开锁中   跳转开锁失败页  
19 再试一次 开锁失败 点击再试一次 跳转至输码开锁页  
20 反馈问题 开锁失败 点击反馈问题 弹出“请填写问题”的对话框  
21 取消 请填写问题对话框 点击取消 返回开锁失败页  
22 输入框测试 单车项目 所有输入框的字符数限制设置为200,没有输入或只输入空格,不能提交(提交按钮置灰) 输入为空,提交 提交置灰  
23 输入框测试 单车项目 所有输入框的字符数限制设置为200,没有输入或只输入空格,不能提交(提交按钮置灰) 输入空格,提交 置灰  
24 输入框测试 单车项目 所有输入框的字符数限制设置为200,没有输入或只输入空格,不能提交(提交按钮置灰) 尝试输入201个字符 字符限制  
25 输入框测试 单车项目 所有输入框的字符数限制设置为200,没有输入或只输入空格,不能提交(提交按钮置灰) 输入10个字符,提交 提交成功,数据库验证  
26 使用中 开锁成功 点击支付订单 显示时间、里程、卡路里、速度、已消费金额、管理员号码、结束骑行按钮  
27 结束骑行 使用中,时间超过三分钟 点击结束骑行 弹出确认信息提示  
28 取消 弹出确认信息提示 点击取消 返回到使用中页面  
29 确定 弹出确认信息提示 点击确定 跳转到订单详情页  
30 结束骑行 押金缴纳 使用中,时间未超过三分钟 点击结束骑行 弹出对话框,请填写原因  
31 取消 骑行未超过三分钟,对话框 点击取消 返回到使用中页面  
32 确定 骑行未超过三分钟,对话框 下拉选择,机器出现故障项 有输入框  
33   骑行未超过三分钟,对话框 下拉选择非机器出现故障项 无输入框,区域置灰  
34 原因选择 下拉框 骑行未超过三分钟,对话框,点击确定 下拉按钮体验    
  • 功能点--支付
用例标号用例说明前置操作操作预期结果备注
35 去支付 结束骑行-订单详情页 点击去支付 跳转到在线支付页  
36 支付方式 支付宝支付   产生一条链接,点击链接,在浏览器中完成支付  
37 支付方式 微信支付   默认支付方式  
38 体验反馈 支付成功 点击体验反馈 意见反馈页,输入框  
39 意见反馈页输入框 单车项目 所有输入框的字符数限制设置为200,没有输入或只输入空格,不能提交(提交按钮置灰) 输入为空,提交 提交置灰  
40 意见反馈页输入框   输入空格,提交 置灰  
41 意见反馈页输入框   尝试输入201个字符 字符限制  
42 意见反馈页输入框   输入10个字符 提交成功,数据库验证  
43 返回首页 支付成功 点击返回首页 返回首页  
44 重新支付 支付失败 点击重新支付 跳转到在线支付页  
45 返回首页 支付失败 点击返回首页 跳转到首页  

测试用例设计:一串数字,闰年的判别

参考回答:

判断闰年的标准是:能整除4且不能整除100,能整除400。设定合法的年份为1-9999
public class Test2 {
public static void main(String[] args) {
Scanner in = new Scanner (System.in);
int year=in.nextInt();
if(year<=0||year>9999)
{
System.out.println("请输入正确的年份");
}
if((year%4==0&&year%100!=0)||year%400==0)
{
System.out.println("闰年");
}else
{
System.out.println("不是闰年");
}
}
}

测试用例:

测试用例

输入

预期输出

被 4 整除, 但是不被100 整除的年份

2008

闰年

被 4 整除, 同时被100 整除的年份,且被 400 整除的年份

2000

闰年

被 4 整除, 同时被100 整除的年份,但是不被400 整除的年份

1900

不是闰年

偶数, 不被4 整除的年份

2022

不是闰年

奇数年份

1999

不是闰年

年份大于9999

10000

请输入正确的年份

年份小于0

0

请输入正确的年份

怎么看待软件测试的潜力和挑战?

软件测试是正在快速发展,充满挑战的领域。尽管现在许多自动化测试软件的出现使得传统手工测试的方式被代替,但自动化测试工具的开发等专项测试中仍然需要大量具有专业技能与专业素养的测试人员,并且随着云计算、物联网、大数据的发展,传统的测试技术可能不再适用,测试人员也因此面临着挑战,需要深入了解新场景并针对不同场景尝试新的测试方法,同时敏捷测试、Devops的出现也显示了软件测试的潜力。

软件测试的核心竞争力是什么?

测试人员的核心竞争力在于提早发现问题,并能够发现别人无法发现的问题。

1、早发现问题:问题发现的越早,解决的成本越低。如果一个需求在还未实现的时候就能发现需求的漏洞,那么这种问题的价值是最高的。

2、发现别人无法发现的问题:所有人都能发现的问题,你发现了,那就证明你是可以被替代的。别人发现不了,而你可以发现,那么你就是无法被替代。

你觉得测试项目具体工作是什么?

搭建测试环境、撰写测试用例、执行测试用例、写测试计划,测试报告、测试,并提交BUG表单、跟踪bug修改情况

执行自动化测试,编写脚本,执行,分析,报告

进行性能测试,压力测试等其他测试,执行,分析,调优,报告

自动化测试有什么意义,都需要做些什么?

1、可以对程序的新版本自动执行回归测试

2、可以执行手工测试困难或者不可能实现的测试,如压力测试,并发测试,

3、能够更好的利用资源,节省时间和人力

直播测试:

  • 秒开:指直播页面的首屏快速的展现出来(1S左右)。
  • 分辨率:影响图像大小,与图像大小成正比;分辨率越高,图像越大;分辨率越低,图像越小。
  • 帧率:所谓的测量单位为每秒显示帧数(Frames per Second,简称:FPS)或“赫兹”(Hz)。影响画面流畅度,与画面流畅度成正比:帧率越大,画面越流畅;帧率越小,画面越有跳动感。
  • 清晰度:清晰度指影像上各细部影纹及其边界的清晰程度,现阶段对直播清晰度的考量还是主要通过人眼观察。
  1. 播放的模式:横屏,竖屏,或者来回切换。
  2. 播放的网络:WIFI、2G、3G、4G网络环境下的播放和加载情况。断网之后能否继续恢复播放
  3. 播放的UI键位:返回,关闭,播放/暂停,最大化/最小化,音量的调节。
  4. 播放的机制:首次进入正常播放 ,暂停播放,继续播放,快进播放,倍速播放,连续播放,拖拽播放等等情况
  5. 播放延迟情况:对于直播,要求延迟再3秒内,最好是1秒
  6. 播放中的互动:互动,评价等是否正常
  7. 播放缓存机制:如支持缓存下载,则校验下载,下载完成播放,下载暂停,下载继续,下载删除再下载等情况
  8. 异常情况:播放中多个APP前后切换,播放是否正常,音源占用
  9. 异常情况:播放中被外界打断,如来电,手机低电提醒或关机,短信,按home键,分屏模式,突然有第三方消息,等

微信红包测试:

功能测试

提醒一定要先看有没有绑定银行卡,信用卡不行

  1. 红包金额输入框是否只能输入数字和小数点,小数位数是否有限制,如果直接输入点,点前是否有0
  2. 红包个数输入框中是否只能输入数字
  3. 红包金额框输入的数字,最多200,最少0.01,如果不在该范围内是否有提示
  4. 红包个数框输入的数字,最多100,最少1,如果不在该范围内是否有提示
  5. 红包的描述框是否可以输入英文、中文、符号、数字、特殊字符、表情等,他们是否可以搭配使用,是否可以删除
  6. 以上提及的输入框是否允许复制粘贴操作
  7. 是否可以再普通红包和拼手气红包之间进行切换
  8. 按返回键是否可以取消发红包
  9. 支付过程中,是否会自动匹配支付方式,余额不足时,是否提示换卡支付,可否自己选择支付方式
  10. 输入支付密码时,是否可以通过密码输入或者指纹支付方式支付
  11. 支付成功后,是否可以自己返回到聊天界面中
  12. 红包发送成功后,别人能否正常领取,在个数>=2的情况下,自己是否可以领取
  13. 同一个人是否可以多次抢同一个红包
  14. 24小时后,未领取的红包是否还可以领取,未领取的红包是否会退还到原先的账户上
  15. 红包记录中的收发记录与实际收发记录是否一致
  16. 是否可以连续多次发红包

兼容性测试

  • 安卓系统、苹果系统是否都可以发红包
  • 电脑端是否可以抢红包

性能测试

  • 不同网速下发红包的时间,尤其弱网情况下发红包的时间
  • 发红包之后跳转到聊天界面的时间
  • 发红包时的耗电量
  • 退款到账时间

界面测试

  • 发红包的界面有没有错别字
  • 发红包的界面是否排版合理
  • 发红包的界面颜色搭配是否合理

安全测试

  • 微信号多人且异地登录,是否会有提示信息
  • 红包发送成功后,是否收到微信支付的通知消息
  • 红包被领取后,可领取的红包数和金额变少
  • 红包发送失败,余额与银行卡中的钱不会变少

如何对一瓶矿泉水进行测试

参考回答:

界面测试:查看外观是否美观

功能度:查看水瓶漏不漏;瓶中水能不能被喝到

安全性:瓶子的材质有没有毒或细菌

可靠性:从不同高度落下的损坏程度

可移植性:再不同的地方、温度等环境下是否都可以正常使用

兼容性:是否能够容纳果汁、白水、酒精、汽油等

易用性:是否烫手、是否有防滑措施、是否方便饮用

用户文档:使用手册是否对的用法、限制、使用条件等有详细描述

疲劳测试:将盛上水(案例一)放24小时检查泄漏时间和情况;盛上汽油(案例二)放24小时检查泄漏时间和情况等

压力测试:用根针并在针上面不断加重量,看压强多大时会穿透

跌落测试:测试在何种高度跌落会破坏水瓶

测试笔?

功能性:
笔是不是能正常写出字来?是否可以正常关上笔帽?是不是不会漏墨水?如果是铅笔,字迹是否可以被擦出?

性能:

正常人用该笔写字可以达到什么速度?孩子呢?老人呢?左撇子呢?

压力测试:
使劲用该笔写字是否会断裂?弹簧圆珠笔,如果以极快的速度按压弹簧,该笔是否会崩溃?

安全性测试:
该笔从桌子上自由落体,是否会爆炸?是否会爆墨水?该笔从二十楼的高度落下呢?该笔放在一定倾角的桌面上是否会滑落?笔的材料是否可以?

适配性测试:
该笔是否可以装入常规的笔袋里面?是否适合孩子和老人使用?左撇子呢?

兼容性测试:
该笔是否可以适应多种不同品牌的墨水?该笔是否可以在各种纸面上流利书写,甚至地板,墙面和树皮上?

易用性+界面:
该笔的颜色和图案是否会引起不适?该笔的粗度如何,手感如何,握感如何?用户在不经过任何教程说明的情况下,是否可以正确使用?

对水壶进行测试? 

1.功能

(1)水倒水壶容量的一半

(2)水倒规定的安全线

(4)水壶容量刻度与其他水壶一致

(5)盖子拧紧水倒不出来

(6)烫手验证

2.性能

(1)使用最大次数或时间

(2)掉地上不易损坏

(3)盖子拧到什么程度水倒不出来

(4)保温时间长

(5)壶的耐热性

(6)壶的耐寒性

(7)长时间放置水不会漏

(8)壶上放置重物达到什么程度壶会被损坏

3.界面

(1)外观完整、美观

(2)大小与设计一样(高、宽、容量、直径)

(3)拿着舒服

(4)材质与设计一样

(5)壶上的图案掉落

(6)图案遇水溶解

4.安全

(1)壶使用的材质毒或细菌的验证

(2)高温材质释放毒性

(3)低温材质释放毒性

5.易用性

(1)倒水方便

(2)喝水方便

(3)携带方便

(4)使用简单,容易操作

(5)防滑措施

6.兼容性

(1)壶能够容纳果汁、白水、酒精、汽油等。

7.震动测试

(1)壶加包装(有填充物),六面震动,检查产品是否能应对铁路/公路/航空运输。

8.可移植性

(1)壶在不同地方、温度环境下都可以正常使用。

 

对一个页面进行测试?

1、UI测试:页面布局、页面样式检查、控件长度是否够长;显示时,是否会被截断;支持的快捷键,Tab键切换焦点顺序正确性等。

2、功能测试:页面上各类控件的测试范围,测试点。结合控件的实际作用来补充检查点: 比如, 密码框是否*显示, 输入是否做trim处理等。

3、安全测试:输入特殊字符,sql注入,脚本注入测试。后台验证测试,对于较重要的表单 ,绕过js检验后台是否验证;数据传输是否加密处理,比如, 直接请求转发,地址栏直接显示发送字符串?

4、兼容性测试

5、性能测试

测试图片:

先说说下简要的需求:

能上传jpg、gif、bmp、png格式的图片,大小为1K-1M

需要检查:

  1. 0k 大小的jpg、gif、bmp、png图片的上传
  2. 小于1k 的jpg、gif、bmp、png图片的上传
  3. 1k的jpg、gif、bmp、png 图片的上传
  4. 大于1k小于1M的jpg、gif、bmp、png图片的上传
  5. 大于1M的jpg、gif、bmp、png图片的上传
  6. 其他格式的图片的上传
  7. 不选择图片直接上传
  8. 上传同名的图片
  9. 上传图片名称为特殊字符,如文件名含有(‘符号等。
  10. 上传图片名称为最大文件名称(过长文件名)
  11. 上传图片的路径过长
  12. 上传中文名称的图片
  13. 查看上传的图片是否能正常显示
  14. 各种大小规格图片上传,查看是否正常显示
  15. 上传的图片是否能正常删除
  16. 查看上传图片是进度条显示是否正确
  17. 上传图片后点击进度条后的取消,查看是否能正常取消图片

 对于其他特殊要求的验证:

  1. 是否能一次选择多个图片后同时上传
  2. 如果支持大图片上传或上传网络缓慢,上传的时间超出了用户自动退出登录时间,是否能上传成功

对朋友圈点赞功能进行测试

参考回答:

1.是否可以正常点赞和取消;

2.点赞的人是否在可见分组里;

3.点赞状态是否能即时更新显示;

4.点赞状态,共同好友是否可见;

6.性能检测,网速快慢对其影响;

7.点赞显示的是否正确,一行几个;

8.点赞是否按时间进行排序,头像对应的是否正确;

9.是否能在消息列表中显示点赞人的昵称、5.不同手机,系统显示界面如何;

备注;

10.可扩展性测试,点赞后是否能发表评论;

11.是否在未登录时可查看被点赞的信息。

如何模拟弱网测试jmeter  Fiddler模拟弱网

请你回答一下如何测试手机开机键?

功能测试:

按下开机键,屏幕能否亮起

性能测试:

按下开机键,屏幕能否在规定时间内亮起

压力测试

连续多次按下开机键,观察屏幕是否能一直亮起,到多久时间失灵

健壮性测试

给定一个中了病毒的手机或者是淘汰许久的老机子,安歇开机键观察屏幕能否亮起

可靠性测试

连续按下开机键有限次数,比如1万次,记录屏幕未亮起的次数

可用性测试

开机键按下费不费力,开机键的形状设计是否贴合手指,开机键的位置设计是否方便

 如何对淘宝搜索框进行测试

一, 功能测试

1. 输入关键字,查看: 返回结果是否准确,返回的文本长度需限制

1.1输入可查到结果的正常关键字、词、语句,检索到的内容、链接正确性;

1.2输入不可查到结果的关键字、词、语句;

1.3输入一些特殊的内容,如空、特殊符、标点符、极限值等,可引入等价类划分的方法等;

2. 结果显示:标题,卖家,销售量,单行/多行,是否有图片

3. 结果排序:价格 销量 评价 综合

4.返回结果庞大时,限制第一页的现实量,需支持翻页

5. 多选项搜索:关键字 品牌 产地 价格区间 是否天猫 是否全国购

6. 是否支持模糊搜索,支持通配符的查询

7, 网速慢的情况下的搜索

8. 搜索结果为空的情况

9. 未登录情况和登录情况下的搜索(登录情况下 存储用户搜索的关键字/搜索习惯)

二.性能测试:

1压力测试:在不同发用户数压力下的表现(评价指标如响应时间等)

2负载测试:看极限能承载多大的用户量同时正常使用

3稳定性测试:常规压力下能保持多久持续稳定运行

4内存测试:有无内存泄漏现象

5大数据量测试:如模拟从庞大的海量数据中搜索结果、或搜索出海量的结果后列示出来,看表现如何等等。

三. 易用性:交互界面的设计是否便于、易于使用

1依据不同的查询结果会有相关的人性化提示,查不到时告知?查到时统计条数并告知?有疑似输入条件错误时提示可能正确的输入项等等处理;

2查询出的结果罗列有序,如按点击率或其他排序规则,确保每次查询出的结果位置按规则列示方便定位,显示字体、字号、色彩便于识别等等;

3标题查询、全文检索、模糊查询、容错查询、多关键字组织查询(空格间格开)等实用的检索方式是否正常?

4输入搜索条件的控件风格设计、位置摆放是否醒目便于使用者注意到,有否快照等快捷查看方式等人性化设计?

四. 兼容性

1WINDOWS/LINUX/UNIX等各类操作系统下及各版本条件下的应用

2IE/FIREFOX/GOOGLE/360/QQ等各类浏览器下及各版本条件下、各种显示分辨率条件下的应用

3SQL/ORACLE/DB2/MYSQL等各类数据库存储情况下的兼容性测试

4简体中文、繁体中文、英文等各类语种软件平台下的兼容性测试

5IPHONE/IPAD、安卓等各类移动应用平台下的兼容性测试

6与各相关的监控程序的兼容性测试,如输入法、杀毒、监控、防火墙等工具同时使用

五. 安全性

1被删除、加密、授权的数据,不允许被SQL注入等攻击方式查出来的,是否有安全控制设计;

2录入一些数据库查询的保留字符,如单引号、%等等,造成查询SQL拼接出的语句产生漏洞,如可以查出所有数据等等,这方面要有一些黑客攻击的思想并引入一些工具和技术,如爬网等。

3通过白盒测试技术,检查一下在程序设计上是否存在安全方面的隐患;

4对涉及国家安全、法律禁止的内容是否进行了相关的过滤和控制;

简单用户界面登陆过程都需要做哪些分析?

一、功能测试

1.输入正确的用户名和密码,点击提交按钮,验证是否能正确登录。

2.输入错误的用户名或者密码,验证登录会失败,并且提示相应的错误信息。

3.登录成功后能否能否跳转到正确的页面

4.用户名和密码,如果太短或者太长,应该怎么处理

5.用户名和密码,中有特殊字符(比如空格),和其他非英文的情况

6.记住用户名的功能

7.登陆失败后,不能记录密码的功能

8.用户名和密码前后有空格的处理

9.密码是否非明文显示显示,使用星号圆点等符号代替。

10.牵扯到验证码的,还要考虑文字是否扭曲过度导致辨认难度大,考虑颜色(色盲使 用者),刷新或换一个按钮是否好用

11.登录页面中的注册、忘记密码,登出用另一帐号登陆等链接是否正确

12.输入密码的时候,大写键盘开启的时候要有提示信息。

13.什么都不输入,点击提交按钮,检查提示信息。

二、界面测试

1.布局是否合理,testbox和按钮是否整齐。

2.testbox和按钮的长度,高度是否复合要求。

3. 界面的设计风格是否与UI的设计风格统一。

4. 界面中的文字简洁易懂,没有错别字。

三、性能测试

1.打开登录页面,需要的时间是否在需求要求的时间内。

2.输入正确的用户名和密码后,检查登录成功跳转到新页面的时间是否在需求要求的时间内。

3.模拟大量用户同时登陆,检查一定压力下能否正常登陆跳转。

四、安全性测试

1.登录成功后生成的Cookie,是否是httponly (否则容易被脚本盗取)。

2.用户名和密码是否通过加密的方式,发送给Web服务器。

3.用户名和密码的验证,应该是用服务器端验证, 而不能单单是在客户端用javascript 验证。

4.用户名和密码的输入框,应该屏蔽SQL注入攻击。

5.用户名和密码的的输入框,应该禁止输入脚本 (防止XSS攻击)。

6.防止暴力破解,检测是否有错误登陆的次数限制。

7. 是否支持多用户在同一机器上登录。

8. 同一用户能否在多台机器上登录。

五、可用性测试

1. 是否可以全用键盘操作,是否有快捷键。

2. 输入用户名,密码后按回车,是否可以登陆。

3. 输入框能否可以以Tab键切换。

六、兼容性测试

1.不同浏览器下能否显示正常且功能正常(IE,6,7,8,9, Firefox, Chrome, Safari,等)。

2.同种浏览器不同版本下能否显示正常且功能正常。

2.不同的平台是否能正常工作,比如Windows, Mac。

3.移动设备上是否正常工作,比如Iphone, Andriod。

4.不同的分辨率下显示是否正常。

七、本地化测试

1. 不同语言环境下,页面的显示是否正确。

如果给你一台电梯,请问你如何测试它,分析如下:

  1. 功能:上升、下降、停止、开门、关门、梯内电话、灯光、指示灯等;
  2. 性能:速度、反应时间、关门时间等;
  3. 压力:超载、尖锐物碰撞电梯壁等;
  4. 安全:停电、报警装置、轿箱停靠位置、有人扒门时的情况等;
  5. 可用性:按键高度、操作是否方便、舒适程度等;
  6. UI:美观程度、光滑程度、形状、质感等;
  7. 稳定性:长时间运行情况等;
  8. 兼容性:不同电压是否可工作、不同类型电话是否可安装等
  9. 下面是详细的测试点:
  10. 需求测试: 查看电梯使用说明书、安全说明书等
  11. 界面测试: 查看电梯外观

  12. 功能测试:

  13. 测试电梯能否实现正常的上升和下降功能。
    1. 电梯的按钮是否都可以使用
      电梯内分楼层键是否正常
      电梯内开关门键是否正常
      电梯内的报警键是否正常使用
      电梯外的上下键是否正常

    2. 电梯门的打开,关闭是否正常。

    3. 报警装置是否可用。
    4. 与其他电梯之间是否协作良好。
    5. 通风状况如何。
    6. 突然停电时的情况。
    7. 关注显示屏,电梯内外的显示屏显示的电梯层数、运行方向是否正常
    8. 有障碍物时,电梯门的感应系统是否有效

    9. 上升途中的响应。
      电梯本来在1楼,如果有人按18楼,那么电梯在上升到5楼的时候,有人按了10楼,这时候是否会在10楼先停下来;
      电梯下降到10层时显示满员,此时若8层有人等待电梯,是否在8层停。

    10. 是否有手机信号
    可靠性:
    1. 门关上的一刹那出现障碍物。
    2. 同时按关门和开门按钮。
    3. 点击当前楼层号码
    4. 多次点击同一楼层号码
    5. 同时按上键和下键
    易用性:
    1. 电梯的按钮的设计符合一般人的习惯吗
    2. 楼层按键高度(小孩和一些身高矮的用户会按键不方便)
    3. 电梯是否有地毯、夏天是否有空调、通风条件、照明条件、手机信号是否通畅
    4. 电梯是否有扶手,是否有专针对残疾人的扶手等等
    压力测试:
    1. 看电梯的最大承重量,在负载过重时报警装置是否有提醒
    2. 在一定时间内不断让电梯上升、下降
    稳定性测试:

    看电梯在最大负载下平稳运行的最长时间

    兼容性测试
    1. 电梯的整体和其他设备的兼容性,与大楼的兼容,与海地隧道的兼容等等
    2. 不同类型的电压是否兼容
    安全性测试
    1. 下坠时是否有制动装置
    2. 暴力破坏电梯时是否报警,超重是否报警
    3. 停电情况下电梯是否有应急电源装置
    性能测试
    1. 测试电梯负载单人时的运行情况(基准测试)
    2. 多人时的运行情况(负载测试)
    3. 一定人数下较长时间的运作(稳定性测试)
    4. 更长时间运作时的运行情况(疲劳测试)
    5. 不断增加人数导致电梯报警(拐点压力测试)

测试搜索框:

一般这种问题,首先就是分类

一般分:功能测试、性能测试、易用性测试、界面测试、安全性测试、兼容性测试、其它测试

功能测试
搜索内容为空,点击“百度一下”,看系统处理
搜索内容为空格,点击“百度一下”,看系统处理
搜索内容含有特殊字符的处理
搜索内容带有非法字符
搜索长度在规定范围之内
搜索长度在规定范围之外,看系统能否正确进行截取
搜索内容输入敏感词汇,查看系统处理
搜索框是否支持快捷键操作,比如:zhantie
是否支持回车键搜索
性能测试
在网络情况良好的前提下,页面的跳转需要多少时间
在网络不好的情况下,页面的跳转需要多少时间
对搜索引擎进行加压测试
兼容性测试
不同的平台:windows系统,mac系统
不同的浏览器:Firefox浏览器、Chrome浏览器、IE
不同的移动平台:android、ios
界面测试
查看UI是否显示正确,布局是否美观
页面上是否有错别字
查看错误提示信息的位置是否合理、是否有错别字
搜索结果的页面是否美观
搜索结果数量庞大的时候,是否分页处理,分页是否和需求说明书的要求保持一致
光标的定焦是否准确
安全性测试
敏感内容的检索是禁止的
SQL注入防范

购物车的测试用例:

界面测试

•    界面布局、排版是否合理;文字是否显示清晰;不同卖家的商品是否区分明显。

2.功能测试

未登录时:

•    将商品加入购物车,页面跳转到登录页面,登录成功后购物车数量增加;

•    点击购物车菜单,页面跳转到登录页面。

登录后:

•    所有链接是否跳转正确;

•    商品是否可以成功加入购物车;

•    购物车商品总数是否有限制;

•    商品总数是 否正确;

•    全选功能是否好用;

•    删除功能是否好用;

•    填写委托单功能是否好用;

•    委托单中填写的价格是否正确显示;

•    价格总计是否正确;

•    商品文字太长时是否显示完整;

•    店铺名字太长时是否显示完整;

•    创新券商品是否打标;

•    购物车中下架的商品是否有特殊标识;

•    新加入购物车商品排序(添加购物车中存在店铺的商品和购物车中不存在店铺的商品);

•    是否支持TAB、ENTER等快捷键;

•    商品删除后商品总数是否减少;

•    购物车结算功能是否好用。

3.兼容性测试

•    不同浏览器测试。

4.易用性测试

•    删除功能是否有提示;是否有回到顶部的功能;商品过多时结算按钮是否可以浮动显示。

5.性能测试

•    压力测试;并发测试。

自动售货机:

首先,拆解自动售货机为不同模块,存放货物区、取货区、付钱区

然后,分别从界面、功能、性能、易用性、安全性、正常流程、异常流程处理等方面考虑

--外观设计是否合理,要符合大众的审美

--不同模块的大小分配是否合理

--操作是否简便,流程是否复杂,使用说明是否通俗易懂

--对存放的货物是否有要求,冷藏 or 常温

--货物的外形比较奇怪(过大、过小、形状不规范等),是否能正常出货

--是否可以一次性买很多个物品

--付钱的方式,纸币、硬币、支付宝、微信支付等等

--付了钱选择了货物,是否能正常出货

--付钱不够怎么办

--付钱多了怎么办

--付钱多了但是没有零钱找怎么办

--付钱不是正常的钱怎么办,比如钱大小的纸,游戏币等

--取货口是否好拿货物

--针对不同的人群,是否使用方便,孕妇、儿童、老人、成年人

--货物卡住了或者钱没有退,是否有联系客服的方式

--外壳和钱箱是否设计的比较安全

--异常断电、系统故障,是否有恢复功能

怎么看待测试,知道哪些测试的类型,有用过哪些测试方法?

测试是软件开发中不可或缺的一环,测试通过经济,高效的方法,捕捉软件中的错误,从而达到保重软件内在质量的目的。

测试分为功能测试和非功能测试,非功能测试又可以分为性能测试、压力测试、容量测试、健壮性测试、安全性测试、可靠性测试、恢复性测试、备份测试、协议测试、兼容性测试、可用性测试、配置测试、GUI测试。

测试方法用过等价划分法、边值分析法、错误推测法、因果图法。

 

测试认为bug,但是开发不认为是bug,怎么办?

自身:反复确认,描述准确bug ,开发和自己对需求不了解确认需求说明书或者找产品确认

开发:没有时间改,可以协调;明确他说不是的理由;

在我看来一个软件测试人员需要具备多方面的特质:

● 细心

● 耐心:软件测试,特别是当前国内主流的手动黑盒功能测试。基本上测试的工作就是一项重复劳动,需要有一定的耐心来保证不在枯燥的重复劳动中放过那些细小的缺陷。

● 好奇心:软件测试,是需要保持一颗好奇心的工作。好奇心使得测试人员会多问一个「为什么」,「如果这样,行不行?」。往往这些问题会引导你找到缺陷。
● 会沟通:软件测试人员需要与客户,开发,产品等方方面面保持密切的关系,沟通很重要。良好的沟通过程可以有效地控制成本。
● 总结归纳能力:这跟「会沟通」有关联,软件测试人员需要找到缺陷的真正关键步骤,归纳出缺陷产生的一般规律,总结出一份详尽的测试报告。
● 理解能力:对需求的准确理解,是软件测试人员需要具备的必需条件。
● 表达能力:编写的测试用例什么的只有你自己能读懂可不行。

时间观念:软件测试工作是无止境的,但是软件本身是有交付日期的。软件测试工作需要在保证交付日期之前完成工作,保证软件产出的质量。时间与质量本身需要有一个平衡,为了追求零缺陷而罔顾交付日期的做法是不科学的。前期的制定计划开始,就要对整个过程有一个良好的规划并且按照这个计划的日期来推进。 
责任感:责任感是个系数,责任感与个人资质的乘积才是最终体现到工作中的实际能力。尤其是就目前国内的黑盒手工测试来说,极少有需要特别牛x的人才能干得下来的事情,大家的工作成果差异,常常是态度问题而非能力问题。而很多面试中体现出良好资质的人,放到工作中会发现实际效果不理想,也多与此有关。
 原则性:测试需要一颗有原则的正直的心,不会为了凑数量,将同类问题的变体重复提交;不会因为简单的一句:”这不是问题“而妥协。
学习能力:测试需要不断接触新功能、新理论、新技术、新工具,并非一个省心的活儿。对于学习能力还是有一定的要求的。除了工作相关的以外,开阔的知识面,对于测试人员来说有时也意味着思路的可延展性。 

智力题:8个球找出最轻的一个?

将8个球分成3、3、2三组,
在天平两边分别各放3个称量,会出现两种情况:
(1)一种是如果天平平衡,轻的球就在剩下2个当中,再把这2个球分别放在天平两边进行第二次称量,轻的就在上翘的那边;
(2)另一种是如果天平不平衡,轻的球就在上翘的那边,再把3个当中的任意2个放在天平两边进行第二次称量,如果平衡剩下的那个就是轻的球;如果不平衡,轻的球就在上翘的那边
所以2次就能找出轻的那个.
答:把这些球放在天平上称2次,就能找出轻的球

一根不均匀的绳子,全部烧完需要1个小时,问怎样烧能计时1个小时15分钟?

【答:取出三条绳子。1、同时点燃“第一根的两头”和“第二根的一头”,第一根烧完时间过了“30分钟”;2、第一根烧完后马上点燃第二根的另一头,到第二根烧完时间又过了“15分钟”;3、第二根烧完后马上点燃第三根绳子的两头,当第三根烧完时间又用了“30分钟”。加起来总共=30+15+30=75分钟=一个小时十五分钟。。】

推荐阅读