Java中高级面试题总览

目录

JAVA 基础

JVM 知识

开源框架知识

操作系统

多线程

TCP 与 HTTP

架构设计与分布式

算法

Mysql

中间件

框架


JAVA 基础

1. JAVA 中的几种基本数据类型是什么,各自占用多少字节。

数据类型

关键字

内置类

 

内存占用字节数

布尔型

boolean

Boolean

1字节

字符型

char

Character

2字节

字节型

byte

Byte

1字节

短整型

short

Short

2字节

整形

int

Integer

4字节

长整型

long

Long

8字节

单精度型

float

Float

4字节

双精度型

double

Double

8字节

 

2. String类为什么是final的?

1.final方法比非final快一些
2.final关键字提高了性能。JVM和Java应用都会缓存final变量。
3.final变量可以安全的在多线程环境下进行共享,而不需要额外的同步开销。
4.使用final关键字,JVM会对方法、变量及类进行优化。
5.字符串是不可变的,所以在它创建的时候HashCode就被缓存了,不需要重新计算。这就使得字符串很适合作为Map中的键

https://www.jianshu.com/p/9c7f5daac283

https://mp.weixin.qq.com/s/miAeWHoVCOfM3MDJsxjlFg

 

3. String,Stringbuffer,StringBuilder的区别。

1.可变与不可变

  String类中使用字符数组保存字符串,如下就是,因为有“final”修饰符,所以可以知道string对象是不可变的。

    private final char value[];

  StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也
是使用字符数组保存字符串,如下就是,可知这两种对象都是可变的。

    char[] value;

2.是否多线程安全

  String中的对象是不可变的,也就可以理解为常量,显然线程安全。

  AbstractStringBuilder是StringBuilder与StringBuffer的公共父类,定义了一些字符串的基本操
作,如expandCapacity、append、insert、indexOf等公共方法。

  StringBuffer对方法加了同步锁或者对调用的方法加了同步锁,所以是线程安全的 
 
  StringBuilder并没有对方法进行加同步锁,所以是非线程安全的。

4. ArrayList 和 LinkedList 有什么区别。

1. 是否保证线程安全: ArrayList 和 LinkedList 都是不同步的,也就是不保证线程安全;
2. 底层数据结构: Arraylist 底层使用的是Object数组;LinkedList 底层使用的是双向链表数据结构(注
意双向链表和双向循环链表的区别:);
3. 插入和删除是否受元素位置的影响: ① ArrayList 采用数组存储,所以插入和删除元素的时间复杂度受元
素位置的影响。 比如:执行add(E e)方法的时候, ArrayList 会默认在将指定的元素追加到此列表的末尾,
这种情况时间复杂度就是O(1)。但是如果要在指定位置 i 插入和删除元素的话(add(int index, E 
element))时间复杂度就为 O(n-i)。因为在进行上述操作的时候集合中第 i 和第 i 个元素之后的(n-i)个
元素都要执行向后位/向前移一位的操作。 ② LinkedList 采用链表存储,所以插入,删除元素时间复杂度不
受元素位置的影响,都是近似 O(1)而数组为近似 O(n)。
4. 是否支持快速随机访问: LinkedList 不支持高效的随机元素访问,而 ArrayList 支持。快速随机访问
就是通过元素的序号快速获取元素对象(对应于get(int index)方法)。
5. 内存空间占用: ArrayList的空 间浪费主要体现在在list列表的结尾会预留一定的容量空间,而
LinkedList的空间花费则体现在它的每一个元素都需要消耗比ArrayList更多的空间(因为要存放直接后继和
直接前驱以及数据)。


补充内容:RandomAccess接口

public interface RandomAccess{

}

查看源码我们发现实际上 RandomAccess 接口中什么都没有定义。所以,在我看来 RandomAccess 接口不过
是一个标识罢了。标识什么? 标识实现这个接口的类具有随机访问功能。

在binarySearch()方法中,它要判断传入的list 是否RamdomAccess的实例,如果是,调用
indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法

 
ArraysList 实现了 RandomAccess 接口, 而 LinkedList 没有实现。为什么呢?我觉得还是和底层数据结
构有关!ArraysList 底层是数组,而 LinkedList 底层是链表。数组天然支持随机访问,时间复杂度为 
O(1),所以称为快速随机访问。链表需要遍历到特定位置才能访问特定位置的元素,时间复杂度为 O(n),
所以不支持快速随机访问。,ArraysList 实现了 RandomAccess 接口,就表明了他具有快速随机访问功能。 
RandomAccess 接口只是标识,并不是说 ArraysList 实现 RandomAccess 接口才具有快速随机访问功能
的!


ArrayList:默认长度是10 每次扩容是原来的1.5倍。如果在添加的时候远数组是空的,就直接给一个10的长度,否则的话就加一,当需要的长度大于原来数组长度的时候就需要扩容了
 

下面再总结一下 list 的遍历方式选择:
实现了RadmoAcces接口的list,优先选择普通for循环 ,其次foreach,
未实现RadmoAcces接口的ist, 优先选择iterator遍历(foreach遍历底层也是通过iterator实现的),大
size的数据,千万不要使用普通for循环

5. 讲讲类的实例化顺序,比如父类静态数据,构造函数,字段,子类静态数据,构造函数,字段,当 new 的时候,他们的执行顺序。

父类静态数据 > 子类静态数据 > 父构造函数> 父字段 > 子构造函数 > 子字段

测试代码可以见: 

https://gitee.com/lzhcode/maven-parent/blob/master/lzh-technology/src/main/java/com/lzhsite/technology/grammar/initStatic/

6. 用过哪些 Map 类,都有什么区别,HashMap 是线程安全的吗,并发下使用的 Map 是什么,他们内部原理分别是什么,比如存储方式,hashcode,扩容,默认容量等。

JDK1.8 之前 HashMap 底层是 数组和链表 结合在一起使用也就是 链表散列。HashMap 通过 key 的 
hashCode 经过扰动函数处理过后得到 hash 值,然后通过 (n - 1) & hash 判断当前元素存放的位置(这里的 n 指的时数组的长度),如果当前位置存在元素的话,就判断该元素与要存入的元素的 hash 值以及 key 是否相同,如果相同的话,直接覆盖,不相同就通过拉链法解决冲突。

所谓扰动函数指的就是 HashMap 的 hash 方法。使用 hash 方法也就是扰动函数是为了防止一些实现比较差的 
hashCode() 方法 换句话说使用扰动函数之后可以减少碰撞。
 
JDK1.8之后在解决哈希冲突时有了较大的变化,当链表长度大于阈值(默认为8)时,将链表转化为红黑树,以减少搜索时间。


当 HashMap 中的元素个数超过数组大小 loadFactor时,就会进行数组扩容,loadFactor的默认值为 0.75,这
是一个折中的取值。也就是说,默认情况下,数组大小为 16,那么当 HashMap 中元素个数超过 16*0.75=12 的
时候,就把数组的大小扩展为 2*16=32,即扩大一倍,然后重新计算每个元素在数组中的位置,而这是一个非常
消耗性能的操作,所以如果我们已经预知 HashMap 中元素的个数,那么预设元素的个数能够有效的提高 
HashMap 的性能。


HashMap 包含如下几个构造器:
 
HashMap():构建一个初始容量为 16,负载因子为 0.75 的 HashMap。
HashMap(int initialCapacity):构建一个初始容量为 initialCapacity,负载因子为 0.75 的 HashMap
 

 

7. JAVA8 的 ConcurrentHashMap为什么放弃了分段锁,有什么问题吗,如果你来设计,你如何设计。

从Java 8开始,HashMap,ConcurrentHashMap和LinkedHashMap在处理频繁冲突时将使用平衡树来代替链表,
当同一hash桶中的元素数量超过特定的值便会由链表切换到平衡树,这会将get()方法的性能从O(n)提高到O(logn)。

ConcurrentHashMap适用于读者数量超过写者时,当写者数量大于等于读者时,CHM的性能是低于Hashtable和
synchronized Map的。这是因为当锁住了整个Map时,读操作要等待对同一部分执行写操作的线程结束。CHM适用
于做cache,在程序启动时初始化,之后可以被多个请求线程访问。正如Javadoc说明的那样,CHM是HashTable一
个很好的替代,但要记住,CHM的比HashTable的同步性稍弱


8. 有没有有顺序的Map 实现类,如果有,他们是怎么保证有序的。

在JAVA中,LRU的原生实现是JDK中LinkedHashMap。LinkedHashMap继承自HashMap
【实现原理】 简单说就是HashMap的每个节点做一个双向链表。
每次访问这个节点,就把该节点移动到双向链表的头部。满了以后,就从链表的尾部删除。
但是LinkedHashMap并是非线程安全(其实现中,双向链表的操作是没有任何线程安全的措施的)。
对于线程安全的HashMap,在JDK中有ConcurrentHashMap原生支持。

 

9. 抽象类和接口的区别,类可以继承多个类么,接口可以继承多个接口么,类可以实现多个接口么。

 

10. 继承和聚合的区别在哪。

一、聚合
      
        4.0.0  
        com.juvenxu.mvnbook.account  
        account-aggregator  
        1.0.0-SNAPSHOT  
         pom   
        Account Aggregator  
           
            account-email  
            account-persist  
           
      
 
 注意:packaging的类型为pom ,module的值是一个以当前POM为主目录的相对路径。
 
二、继承
 
可声明父POM供子 POM继承
 
父模块POM如下:

      
        4.0.0  
        com.juvenxu.mvnbook.account  
        account-aggregator  
        1.0.0-SNAPSHOT  
         pom   
        Account Aggregator  
          
            account-email  
            account-persist  
             account-parent  
          
      
 
 子模块声明继承如下:
 
      
        4.0.0  
          
        < parent >  
            com.juvenxu.mvnbook.account  
             account-parent   
            1.0.0-SNAPSHOT  
            < relativePath >../account-parent/pom.xml  
          
          
         account-email   
        Account Email  
      ...  
      
 
 

11. 讲讲你理解的 nio。他和 bio 的区别是啥,谈谈 reactor 模型。 

Reactor模式首先是事件驱动的,有一个或多个并发输入源,有一个Service Handler,有多个Request 
Handlers;这个Service Handler会同步的将
输入的请求(Event)多路复用的分发给相应的Request Handle

http://www.blogjava.net/DLevin/archive/2015/09/02/427045.html

12. 反射的原理,反射创建类实例的三种方式是什么。

    //第一种表示方式--》实际在告诉我们任何一个类都有一个隐含的静态成员变量class
    Class class1 = Foo.class;
    
    //第二种表示方式  已经知道该类的对象通过getClass方法
    Class class2 = foo1.getClass();
 
    //第三种表达方式
    class3 = Class.forName("com.imooc.reflect.Foo");
     

 

13. 反射中,Class.forName 和 ClassLoader 区别。

解释
在java中Class.forName()和ClassLoader都可以对类进行加载。ClassLoader就是遵循双亲委派模型最终调用
启动类加载器的类加载器,实现的功能是“通过一个类的全限定名来获取描述此类的二进制字节流”,获取到二进制
流后放到JVM中。Class.forName()方法实际上也是调用的CLassLoader来实现的。

最后调用的方法是forName0这个方法,在这个forName0方法中的第二个参数被默认设置为了true,这个参数代
表是否对加载的类进行初始化,设置为true时会类进行初始化,代表会执行类中的静态代码块,以及对静态变
量的赋值等操作。

Class.forName(String className);这个方法的源码是

@CallerSensitive
public static Class forName(String className)
                throws ClassNotFoundException {
        Class caller = Reflection.getCallerClass();
        return forName0(className, true, ClassLoader.getClassLoader(caller), caller);
}



应用场景
在我们熟悉的Spring框架中的IOC的实现就是使用的ClassLoader。

而在我们使用JDBC时通常是使用Class.forName()方法来加载数据库连接驱动。这是因为在JDBC规范中明确要
求Driver(数据库驱动)类必须向DriverManager注册自己。

 

14. 描述动态代理的几种实现方式,分别说出相应的优缺点。

Jdk cglib jdk底层是利用反射机制,需要基于接口方式,这是由于 
Proxy.newProxyInstance(target.getClass().getClassLoader(), 
target.getClass().getInterfaces(), this); 
Cglib则是基于asm框架,实现了无反射机制进行代理,利用空间来换取了时间,代理效率高于jdk 

 

15. jdk代理与cglib 实现的区别。

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,
cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,
并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 
采用非常底层的字节码生成技术

16. 为什么CGlib 方式可以对接口实现代理。

 可以,效率低

17. final的用途。

final类不能被继承,没有子类,final类中的方法默认是final的。
final方法不能被子类的方法覆盖,但可以被继承。
final成员变量表示常量,只能被赋值一次,赋值后值不再改变。
final不能用于修饰构造方法

18. 写出三种单例模式实现。

饿汉单例

public class EagerSingleton {
	
	//饿汉单例模式
    //在类加载时就完成了初始化,所以类加载较慢,但获取对象的速度快
    
    private static EagerSingleton instance = new EagerSingleton();//静态私有成员,已初始化
    
    private EagerSingleton() 
    {
        //私有构造函数
    }
    
    public static EagerSingleton getInstance()    //静态,不用同步(类加载时已初始化,不会有多线程的问题)
    {
        return instance;
    }
    
}

 懒汉单例

public class LazySingleton {
	private LazySingleton() {
		System.out.println("LazySingleton is create");
	}

	private static LazySingleton instance = null;

	public static synchronized LazySingleton getInstance() {
		if (instance == null)
			instance = new LazySingleton();
		return instance;
	}
}

 双检锁单例

/**
不可否认,synchronized关键字是可以保证单例,但是程序的性能却不容乐观,
原因在于getInstance()整个方法体都是同步的,这就限定了访问速度。
其实我们需要的仅仅是在首次初始化对象的时候需要同步,
对于之后的获取不需要同步锁。因此,可以做进一步的改进
**/

public class DoubleCheckedLock {  
	
    private static DoubleCheckedLock instance;    
        
    public static DoubleCheckedLock getInstance() {    
        if (instance == null) {  //step1  
            synchronized (DoubleCheckedLock.class) { //step2  
            	 if (instance == null) {  //step3 是不是多余?
                	System.out.println("new DoubleCheckedLock");
                    instance=new DoubleCheckedLock(); //step4  
            	 }
            }  
        }    
        return instance;    
    }    
 
} 

静态内部类模式 

/**
 * INSTANCE在创建过程中是线程安全的,所以说静态内部类形式的单例可保证线程安全,也能保证单例的唯一
 * 性,同时也延迟了单例的实例化。
 *
 **/
public class SingleTon{
  private SingleTon(){}
 
  private static class SingleTonHoler{
     private static SingleTon INSTANCE = new SingleTon();
 }
 
  public static SingleTon getInstance(){
    return SingleTonHoler.INSTANCE;
  }
}

 

19. 如何在父类中为子类自动完成所有的 hashcode 和 equals 实现?这么做有何优劣。

 

20. 请结合 OO 设计理念,谈谈访问修饰符 public、private、protected、default 在应用设计中的作用。

21. 深拷贝和浅拷贝区别。

浅拷贝:①对于数据类型是基本数据类型的成员变量,浅拷贝会直接进行值传递,也就是将该属性值复制一份给新
的对象。因为是两份不同的数据,所以对其中一个对象的该成员变量值进行修改,不会影响另一个对象拷贝得到的
数据。②对于数据类型是引用数据类型的成员变量,比如说成员变量是某个数组、某个类的对象等,那么浅拷贝会
进行引用传递,也就是只是将该成员变量的引用值(内存地址)复制一份给新的对象。因为实际上两个对象的该成
员变量都指向同一个实例。在这种情况下,在一个对象中修改该成员变量会影响到另一个对象的该成员变量值。

深拷贝:不仅要复制对象的所有基本数据类型的成员变量值,还要为所有引用数据类型的成员变量申请存储空间,
并复制每个引用数据类型成员变量所引用的对象,直到该对象可达的所有对象。也就是说,对象进行深拷贝要对整
个对象图进行拷贝!

如果某个属性被transient修饰,那么该属性就无法被拷贝了

22. 数组和链表数据结构描述,各自的时间复杂度。

 

23. error 和 exception 的区别,CheckedException,RuntimeException 的区别。

1.Error(错误)是系统中的错误,程序员是不能改变的和处理的,是在程序编译时出现的错误,只能通过修改程序
才能修正。一般是指与虚拟机相关的问题,如系统崩溃,虚拟机错误,内存空间不足,方法调用栈溢等。对于这类
错误的导致的应用程序中断,仅靠程序本身无法恢复和和预防,遇到这样的错误,建议让程序终止。

2.Exception(异常)表示程序可以处理的异常,可以捕获且可能恢复。遇到这类异常,应该尽可能处理异常,使程
序恢复运行,而不应该随意终止异常。

Exception又分为两类

    CheckedException:(编译时异常) 需要用try——catch显示的捕获,对于可恢复的异常使用CheckedException。

    UnCheckedException(RuntimeException):(运行时异常)不需要捕获,对于程序错误(不可恢
复)的异常使用RuntimeException。

24. 请列出 5 个运行时异常。

NullPointerException - 空指针引用异常
ClassCastException - 类型强制转换异常。
IllegalArgumentException - 传递非法参数异常。
ArithmeticException - 算术运算异常
ArrayStoreException - 向数组中存放与声明类型不兼容对象异常
IndexOutOfBoundsException - 下标越界异常
NegativeArraySizeException - 创建一个大小为负数的数组错误异常
NumberFormatException - 数字格式异常
SecurityException - 安全异常
UnsupportedOperationException - 不支持的操作异常
 

25. 在自己的代码中,如果创建一个 java.lang.String 对象,这个对象是否可以被类加载器加载?为什么。

 

当Java程序请求加载器loader1加载Sample类时,loader1首先委托自己的父加载器去加载Sample类,若父加载
器能加载,则由父加载器完成加载任务,否则才由加载器loader1本身加载Sample类。

加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap 
ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一
次。而加载的顺序是自顶向下,也就是说当发现这个类没有的时候会先去让自己的父类去加载,父类没有再让儿子
去加载

那么在这个例子中我们自己写的String应该是被Bootstrap ClassLoader加载了,所以App 
ClassLoader就不会再去加载我们写的String类了,导致我们写的String类是没有被加载的。

https://blog.csdn.net/u013206465/article/details/47170253

 

26. 说一说你对 java.lang.Object 对象中 hashCode 和 equals 方法的理解。在什么场景下需要重新实现这两个方法。

 

27. 在 jdk1.5 中,引入了泛型,泛型的存在是用来解决什么问题。

 

28. 这样的 a.hashcode() 有什么用,与 a.equals(b)有什么关系。

将对象放入到集合中时,首先判断要放入对象的hashcode值与集合中的任意一个元素的hashcode值是否相
等,如果不相等直接将该对象放入集合中。
如果hashcode值相等,然后再通过equals方法判断要放入对象与集合中的任意一个对象是否相等,如果equals判
断不相等,直接将该元素放入到集合中,否则不放入。

equals与hashcode的关系 
equals相等两个对象,则hashcode一定要相等。但是hashcode相等的两个对象不一定equals相等。 

详情原理请看

https://blog.csdn.net/lzhcoder/article/details/84840418

 

29. 有没有可能 2 个不相等的对象,有相同的 hashcode。

30. Java 中的 HashMap和 HashSet的区别,HashSet内部是如何工作的。

对于 HashSet 而言,它是基于 HashMap 实现的,HashSet 底层使用 HashMap 来保存所有元素,因此 
HashSet 的实现比较简单,相关 HashSet 的操作,基本上都是直接调用底层 HashMap 的相关方法来完成,我
们应该为保存到 HashSet 中的对象覆盖 hashCode() 和 equals()

 http://wiki.jikexueyuan.com/project/java-collection/hashset.html

 

31. 什么是序列化,怎么序列化,为什么序列化,反序列化会遇到什么问题,如何解决。

 

32.Java中按值传递与按引用传递的区别

值传递:(形式参数类型是基本数据类型):方法调用时,实际参数把它的值传递给对应的形式参数,形式参数只是
用实际参数的值初始化自己的存储单元内容,是两个不同的存储单元,所以方法执行中形式参数值的改变不影响实
际参数的值。

引用传递:(形式参数类型是引用数据类型参数):也称为传地址。方法调用时,实际参数是对象(或数组),这时实
际参数与形式参数指向同一个地址,在方法执行中,对形式参数的操作实际上就是对实际参数的操作,这个结果在
方法结束后被保留了下来,所以方法执行中形式参数的改变将会影响实际参数。

 

34创建String的特性

字符串类(Java.lang.String)是Java中使用最多的类,也是最为特殊的一个类,很多时候,我们对它既熟悉
又陌生。在很多面试题中经常用String大做文章,只要掌握了String特性,对付它们就不再是困难了。
1、从根本上认识java.lang.String类和String池
首先,我建议先看看String类的源码实现,这是从本质上认识String类的根本出发点。
从源码中可以看到:
String类是final的,不可被继承。public final class String。
String类是的本质是字符数组char[], 并且其值不可改变。private final char value[];
 
然后打开String类的API文档,从API中可以发现:
String类对象有个特殊的创建的方式,就是直接指定比如String x = "abc","abc"就表示一个字符串对象。
而x是"abc"对象的地址,也叫做"abc"对象的引用。
String对象可以通过“+”串联。串联后会生成新的字符串。也可以通过concat()来串联,这个后面会讲述。
Java运行时会维护一个String Pool(String池),JavaDoc翻译很模糊“字符串缓冲区”。String池用来存放
运行时中产生的各种字符串,并且池中的字符串的内容不重复。而一般对象不存在这个缓冲池,并且创建的对象
仅仅存在于方法的堆栈区。

 
2、String对象的创建的特性
String对象的创建也很讲究,关键是要明白其原理。
 
特性1:
当使用任何方式来创建一个字符串对象s时,Java运行时(运行中JVM)会拿着这个字符串的内容在String池中
找是否存在内容相同的字符串对象,如果不存在,则在池中创建一个字符串s,否则,不在池中添加。
 
特性2:
Java中,只要使用new关键字来创建对象,则一定会(在堆区或栈区)创建一个新的对象。
 
特性3:
使用直接指定、使用纯字符串串联或者在编译期间可以确定结果的变量表达式来创建String对象,则仅仅会检
查维护String池中的字符串,池中没有就在池中创建一个,有则罢了!但绝不会在堆栈区再去创建该String对
象;

特性4:
使用包含编译期间无法确定结果的变量的表达式来创建String对象,则不仅会检查维护String池,而且还会在堆栈区创建一个String对象

示例:
1、 直接指定,例如:下面代码运行结果为true;
String str1 = "abc";
String str2 = "abc";
System.out.println(str1 == str2);
2、 使用纯字符串串联,例如:下面代码运行结果为true;
String str1 = "abc";
String str2 = "ab" + "c";
System.out.println(str1 == str2);
3、 在编译期间可以确定结果的变量表达式,例如:下面代码运行结果为true。
final String str1 = "c"; //final类型的变量在编译时当常量处理
String str2 = "ab" + "c";
String str3 = "ab" + str1;
System.out.println(str2==str3);
 
4、普通变量表达式进行创建字符串,例如:下面代码运行结果为false;
String str1 = "c";
String str2 = "ab" + "c";
String str3 = "ab" + str1;
System.out.println(str2==str3);

示例代码: 

https://gitee.com/lzhcode/maven-parent/blob/master/lzh-technology/src/main/java/com/lzhsite/technology/grammar/TestString.java

33 String的Intern方法有哪些应用

Jdk6 以及以前的版本中,字符串的常量池是放在堆的Perm区的,Perm区是一个类静态的区域(方法区),主要存储
一些加载类的信息,常量池,方法片段等内容,默认大小只有4m
jdk7 主要对 intern 操作和常量池做了以下改动
1.将String常量池从Perm区移动到了Java Heap区 
2.String#intern方法时,如果存在常量池的对象,会直接保存对象的引用,而不会重新创建对象。


String对象的实例调用intern方法后,可以让JVM检查常量池,如果没有实例的value属性对应的字符串序列比
如"123"(注意是检查字符串序列而不是检查实例本身),就将本实例放入常量池,如果有当前实例的value属性对
应的字符串序列"123"在常量池中存在,则返回常量池中"123"对应的实例的引用而不是当前实例的引用,即使当
前实例的value也是"123"。



可以使用string的intern方法来对同一个用户id(字符串)进行加锁,从而确同一个用户的操作的同步的

34、Java中的关键字 transient

Java中transient关键字的作用,简单地说,就是让某些被修饰的成员属性变量不被序列化

https://www.cnblogs.com/chenpi/p/6185773.html

 

35、Reader与InputStream两个类中的read()的区别

InputStream类的read()方法是从流里面取出一个字节,他的函数原型是 int read(); 
Reader类的read()方法则是从流里面取出一个字符(一个char),他的函数原型也是 int read(); 

36、Java Integer的缓存策略 

package com.javapapers.java;
 
public class JavaIntegerCache {
    public static void main(String... strings) {
 
        Integer integer1 = 3;
        Integer integer2 = 3;
 
        if (integer1 == integer2)
            System.out.println("integer1 == integer2");
        else
            System.out.println("integer1 != integer2");
 
        Integer integer3 = 300;
        Integer integer4 = 300;
 
        if (integer3 == integer4)
            System.out.println("integer3 == integer4");
        else
            System.out.println("integer3 != integer4");
 
    }
}

 

大多数人都认为上面的两个判断的结果都是 false。虽然它们的值相等,但由于比较的是对象,而对象的引用不一样,所以会认为两个 if 判断都是 false 的。在 Java 中,== 比较的是对象引用,而 equals 比较的是值。因此,在这个例子中,不同的对象有不同的引用,所以在进行比较的时候都应该返回 false。但是奇怪的是,这里两个相似的 if 条件判断却返回不同的布尔值。

下面是上面代码真正的输出结果,

integer1 == integer2
integer3 != integer4

Java 中 Integer 缓存实现

在 Java 5 中,为 Integer 的操作引入了一个新的特性,用来节省内存和提高性能。整型对象在内部实现中通过使用相同的对象引用实现了缓存和重用。

上面的规则适用于整数区间 -128 到 +127

37.你能用Java覆盖静态方法吗?如果我在子类中创建相同的方法是否会编译时错误?

不,你不能在Java中覆盖静态方法,但在子类中声明一个完全相同的方法不是编译时错误,这称为隐藏在Java中的方法。

JVM 知识

1. 什么情况下会发生栈内存溢出。

至于是堆内存溢出还是方法区内存溢出还是栈内存溢出,其实可以用一些工具比如
JConsole来监视

 

2. JVM 的内存结构,Eden 和 Survivor 比例。

 

3. jvm 中一次完整的 GC 流程是怎样的,对象如何晋升到老年代,说说你知道的几种主要的 jvm 参数。

 

4. 你知道哪几种垃圾收集器,各自的优缺点,重点讲下 cms,包括原理,流程,优缺点

 

5. 垃圾回收算法的实现原理。

 

6. 当出现了内存溢出,你怎么排错。

内存溢出分析
JAVA dump查看线程运行情况:

=====查看栈信息=======================
1.查询java程序pid 
  netstat -ntpl | grep 8080

2.使用jstack [-l] 进程pid > xxx.log将所有线程信息输入到指定文件中

  1)如果程序正常运行:使用jstack [-l] 进程pid > xxx.log将所有线程信息输入到指定文件中 

  2)如果程序无响应:使用 jstack -F [-m] [-l] 进程pid >xxx.log强制打印栈信息 

=====查看堆信息=======================

3.使用top命令找出占用cpu高(或者执行时间很长)的进程pid 

4.使用top -H -p 线程pid 找出占用cpu高(或执行时间长)的线程pid 

5.将占用cpu高的线程pid转换成16进制(window自带计算器) 

6.dump该进程的内存
  jmap -dump:format=b,file=文件名 [进程pid]   将转换后的pid在开始输出的dump文件(xxx.log)中搜索对应线程信息 

7.eclipse Memory Analyzer 对dump文件分析

7. JVM 内存模型的相关知识了解多少,比如重排序,内存屏障,happen-before,主内存,工作内存等。

volatile关键字禁止指令重排序(内存栅栏)有两层意思
1)当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对
后面的操作可见;在其后面的操作肯定还没有进行;

2)在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语
句放到其前面执行。

Happen-Before 原则
1. 程序次序原则:一个线程内,按照程序代码顺序,书写在前面的操作先发生于书写在后面的操作。 
2. volatile 规则:volatile 变量的写,先发生于读,这保证了 volatile 变量的可见性。 
3. 锁规则:解锁(unlock) 必然发生在随后的加锁(lock)前。 
4. 传递性:A先于B,B先于C,那么A必然先于C。 
5. 线程的 start 方法先于他的每一个动作。 
6. 线程的所有操作先于线程的终结。 
7. 线程的中断(interrupt())先于被中断的代码。 
8. 对象的构造函数,结束先于 finalize 方法。

https://blog.csdn.net/lzhcoder/article/details/84726583

8. 简单说说你了解的类加载器。

JVM将类加载过程分为三个步骤:装载(Load),链接(Link)和初始化(Initialize)

链接又分为三个步骤
验证:确保被加载类的正确性;
准备:为类的静态变量分配内存,并将其初始化为默认值;
解析:把类中的符号引用转换为直接引用;


加载过程中会先检查类是否被已加载,检查顺序是自底向上,从Custom ClassLoader到BootStrap 
ClassLoader逐层检查,只要某个classloader已加载就视为已加载此类,保证此类只所有ClassLoader加载一
次。而加载的顺序是自顶向下,也就是由上层来逐层尝试加载此类。


1)Bootstrap ClassLoader
负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类

2)Extension ClassLoader
负责加载java平台中扩展功能的一些jar包,包括$JAVA_HOME中jre/lib/*.jar或-Djava.ext.dirs指定目录下
的jar包

3)App ClassLoader
负责记载classpath中指定的jar包及目录中class

4)Custom ClassLoader
属于应用程序根据自身需要自定义的ClassLoader,如tomcat、jboss都会根据j2ee规范自行实现ClassLoader

 

9.spring里创建出来对象的存活时间

目前的系统,大部分是spring容器的对象,spring默认单实例方式加载,这些对象可能会存在老年代中。

但是方法内部new出来的对象不会存活太长时间,方法结束,引用消息,对象也会在下一次gc被回收。

 

10. 你们线上应用的 JVM 参数有哪些。

-Xms512m 
-Xmx512m 
-XX:MetaspaceSize=256m 
-XX:MaxMetaspaceSize=256m 
-Xmn256m 
-XX:MaxDirectMemorySize=1g 
-XX:SurvivorRatio=10 
-XX:+UseConcMarkSweepGC 
-XX:CMSMaxAbortablePrecleanTime=5000 

##垃圾回收会清理持久代,移除不再使用的classes,只有在 UseConcMarkSweepGC  也启用的情况下才有用
-XX:+CMSClassUnloadingEnabled 

##使用cms作为垃圾回收使用80%后开始CMS收集
-XX:CMSInitiatingOccupancyFraction=80 

##使用手动定义初始化定义开始CMS收集
-XX:+UseCMSInitiatingOccupancyOnly 

##使用并发的方式执行FGC
-XX:+ExplicitGCInvokesConcurrent 

## 设置并行垃圾回收的线程数。此值可以设置与机器处理器数量相等
-XX:ParallelGCThreads=4 

-Xloggc:$work_dir/logs/gc.log 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=$work_dir/logs/java.hprof

11. g1 和 cms 区别,吞吐量优先和响应优先的垃圾收集器选择。

Cms是以获取最短回收停顿时间为目标的收集器。基于标记-清除算法实现。比较占用cpu资源,切易造成碎片。
 
G1是面向服务端的垃圾收集器,是jdk9默认的收集器,基于标记-整理算法实现。可利用多核、多cpu,保留分
代,实现可预测停顿,可控。 

12. 请解释如下 jvm 参数的含义:

-server 
-Xms512m 
-Xmx512m 
-Xss1024K
-XX:PermSize=256m 
-XX:MaxPermSize=512m 
-XX:MaxTenuringThreshold=20 
-XX:CMSInitiatingOccupancyFraction=80 
-XX:+UseCMSInitiatingOccupancyOnly。
 
##设置JVM最大可用内存为3550M。
-Xmx3550m

#设置JVM初始内存为3550m,可以设置与-Xmx相同,以避免每次垃圾回收完成后JVM重新分配内存。
-Xms3550m

#设置每个线程的堆栈大小。在相同物理内 存下,减小这个值能生成更多的线程。但是操作系统对一
#个进程内的线程数还是有限制的,不能无限生成,
#经验值在3000~5000左右
-Xss128k 

#设置持久代大小为16m
-XX:MaxPermSize=16m

#设置垃圾最大年龄。如果设置为0的话,则年轻代对象不经过Survivor区,直
#接进入年老代。对于年老代比较多的应用,可以提高效率。
#如果将此值设置为一个较大值,则年轻代对象会在Survivor区进行多次复制,这样可以增加对象再年轻代的存活
#时间,增加在年轻代即被回收的概论。
-XX:MaxTenuringThreshold=0

系统学习JVM知识这篇文章讲的比较清楚

https://blog.csdn.net/lzhcoder/article/details/86244290

问题和实战

https://mp.weixin.qq.com/s/5lJdrsj_CGnG_w3CjJ8iPg

https://mp.weixin.qq.com/s/sI2qQ8e7TOa4qGqi6-kQgA

 

开源框架知识

1. 简单讲讲 tomcat 结构,以及其类加载器流程。

tomcat结构

Tomcat的核心组件就Connector和Container,一个Connector+一个Container(Engine)构成一个Service,Service就是对外提供服务的组件,有了Service组件Tomcat就能对外提供服务了,但是光有服务还不行,还需要有环境让你提供服务才行,所以最外层的Server就是为Service提供了生存的土壤。


 Container 是容器的父接口,所有子容器都必须实现这个接口,Container 容器的设计用的是典型的责任链的设计模式,它有四个子容器组件构成,分别是:Engine、Host、Context、Wrapper,这四个组件不是平 行的,而是父子关系,Engine 包含 Host,Host 包含 Context,Context 包含 Wrapper。通常一个 Servlet class 对应一个 Wrapper,如果有多个 Servlet 就可以定义多个 Wrapper,如果有多个 Wrapper 就要定义一个更高的 Container 了

文档结构组成:



    
    
    
    
    
    	
        
        
        
            
            
            	
                
                    
                    
                    
                
        
    

在Tomcat 6中默认情况下,Jar包的加载顺序是: 

1)JRE中的Java基础包
2)Web应用WEB-INF\lib下的包
3)Tomcat\lib下的包

线程模型:支持以下四种线程模型。

2. tomcat 如何调优,涉及哪些参数。

 Tomcat调优主要从四个方面考虑:1)、吞吐量。2)、Responsetime。 3)、CPU  load 4)、MemoryUsage。
 ☞ 参数调优:1)、Tomcat启动参数的优化:Tomcat 的启动参数位于tomcat的安装目录\bin目录下,如果你是Linux操作系统就是catalina.sh文件,如果你是Windows操作系统那么你需要改动的就是catalina.bat文件。
   ✔ Linux系统中catalina.sh文件中添加如下参数(重要参数随后说明):

export JAVA_OPTS="-server -Xms1400M -Xmx1400M -Xss512k -XX:+AggressiveOpts -XX:+UseBiasedLocking 
-XX:PermSize=128M -XX:MaxPermSize=256M -XX:+DisableExplicitGC -XX:MaxTenuringThreshold=31
-XX:+UseConcMarkSweepGC -XX:+UseParNewGC  -XX:+CMSParallelRemarkEnabled 
-XX:+UseCMSCompactAtFullCollection -XX:LargePageSizeInBytes=128m  -XX:+UseFastAccessorMethods
-XX:+UseCMSInitiatingOccupancyOnly -Djava.awt.headless=true " 

  ◀ -Server(重要):只要Tomcat是运行在生产环境中,这个参数必须添加。因为Tomcat默认是java-client模式运行,添加server后表示以真实的production的模式运行,将拥有更大、更高的并发处理能力,更快、更强的JVM垃圾回收机制,可以获得更多的负载和吞吐量等等。
  ◀ -Xms -Xmx:既JVM内存设置了,把Xms与Xmx两个值设成一样是最优的做法。(否则当内存=Xmx向Xms变化时,CPU高速运转触发垃圾回收机制,严重时会导致系统‘卡壳’,因此一开始我们就把这两个设成一样,使得Tomcat在启动时就为最大化参数充分利用系统的效率。)
   ※在设这个最大内存即Xmx值时请先打开一个命令行:能够正常显示JDK的版本信息,说明这个值能够用。
   
  ◀ -Xmn:设置年轻代大小为512m。整个堆大小=年轻代 + 老年代 + 持久代。持久代一般固定大小为64m,所以增大年轻代后,将会减小年老代。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。
  ◀ -Xss:是指设定每个线程的堆栈大小。这个就要依据程序,看一个线程大约需要占用多少内存,可能会有多少线程同时运行等。一般不易设置超过1M,要不然容易出现out ofmemory。
2)、Tomcat容器内优化:打开tomcat安装目录\conf\server.xml文件。   其中如下参数的默认值远远不够我们使用,我们对其进行了更改,更改后的配置如下:

  ▶ URIEncoding=”UTF-8”:使得tomcat可以解析含有中文名的文件的url。
  ▶ minSpareThreads:最小备用线程数,tomcat启动时的初始化的线程数。
  ▶ maxSpareThreads:如果空闲状态的线程数多于设置的数目,则将这些线程中止,减少这个池中的线程总数。
  ▶ connectionTimeout:网络连接超时时间毫秒数。
  ▶ maxThreads:Tomcat使用线程来处理接收的每个请求。这个值表示Tomcat可创建的最大的线程数,即最大并发数
  ▶ acceptCount:当线程数达到maxThreads后,后续请求会被放入一个等待队列,这个acceptCount是这个队列的大小,如果这个队列也满了,就直接refuse connection
  ▶ maxProcessors与minProcessors:在 Java中线程是程序运行时的路径,是在一个程序中与其它控制线程无关的、能够独立运行的代码段。它们共享相同的地址空间。多线程帮助程序员写出CPU最大利用率的高效程序,使空闲时间保持最低,从而接受更多的请求。 通常Windows是1000个左右,Linux是2000个左右。
  ▶ useURIValidationHack:设成"false",可以减少它对一些url的不必要的检查从而减省开销。
  ▶ enableLookups:设置为"false",主要为了消除DNS查询对性能的影响我们可以关闭DNS查询,方式是修改server.xml文件中的enableLookups参数值
  ▶ disableUploadTimeout:允许Servlet容器,正在执行使用一个较长的连接超时值,以使Servlet有较长的时间来完成它的执行,默认值为false
  ▶ 给Tomcat配置gzip压缩(HTTP压缩)功能:HTTP 压缩可以大大提高浏览网站的速度,它的原理是,在客户端请求网页后,从服务器端将网页文件压缩,再下载到客户端,由客户端的浏览器负责解压缩并浏览。相对于普通的浏览过程HTML、CSS、Javascript、Text ,它可以节省40%左右的流量。更为重要的是,它可以对动态生成的,包括CGI、PHP、JSP、ASP、 Servlet、SHTML等输出的网页也能进行压缩,压缩效率惊人。
    1)、compression="on" 打开压缩功能
    2)、compressionMinSize="2048" 启用压缩的输出内容大小,这里面默认为2KB
    3)、noCompressionUserAgents="gozilla, traviata" 对于以下的浏览器,不启用压缩
    4)、compressableMimeType="text/html,text/xml" 压缩类型
  ▶ redirectPort: 如果我们走https协议的话,我们将会用到8443端口这个段的配置。
 

3. 讲讲 Spring 加载流程。

 

4. 讲讲 Spring 事务的传播属性。

PROPAGATION_REQUIRED--支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。

 

5. Spring 如何管理事务的。

 

6. Spring 怎么配置事务(具体说出一些关键的 xml元素)。

tx:advice,aop:config

 

7. 说说你对 Spring 的理解,非单例注入的原理?它的生命周期?循环注入的原理,aop 的实现原理,说说 aop 中的几个术语,它们是怎么相互工作的。

 

8. SpringMVC 中 DispatcherServlet初始化过程。

1. 用户向服务器发送请求,请求被Spring 前端控制Servelt DispatcherServlet捕获;
2. DispatcherServlet对请求URL进行解析,得到请求资源标识符(URI)。然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括Handler对象以及Handler对象对应的拦截器),最后以HandlerExecutionChain对象的形式返回;
3. DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。(附注:如果成功获得HandlerAdapter后,此时将开始执行拦截器的preHandler(...)方法)
4.  提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
      HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
      数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
      数据根式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
      数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中
5.  Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象;
6.  根据返回的ModelAndView,选择一个适合的ViewResolver(必须是已经注册到Spring容器中的ViewResolver)返回给DispatcherServlet ;
7. ViewResolver 结合Model和View,来渲染视图


 

操作系统

1. Linux 系统下你关注过哪些内核参数,说说你知道的。

 

2. Linux 下 IO 模型有几种,各自的含义是什么。

1.BIO(blocking IO):同步阻塞 I/O
2.NIO(nonblocking IO):同步非阻塞 I/O
3.多路复用IO( IO multiplexing)
4.信号驱动I/O( signal driven IO)
5.异步 I/O(asynchronous IO)

https://blog.csdn.net/baiye_xing/article/details/74331041

3. epoll 和 poll 有什么区别。

select、poll、epoll都是IO多路复用的机制,先是监听多个文件描述符FD,一旦某个FD就绪,就可以进行相应
的读写操作
。但是select、poll、epoll本质都是同步I/O,他们都需要在读写事件就绪之后自己负责读写,即这
个读写过程是阻塞的


总结:
(1)select,poll实现需要自己不断轮询所有fd集合,直到设备就绪,期间可能要睡眠和唤醒多次交替。而
epoll其实也需要调用 epoll_wait不断轮询就绪链表,期间也可能多次睡眠和唤醒交替,但是它是设备就绪时,
调用回调函数,把就绪fd放入就绪链表中,并唤醒在 epoll_wait中进入睡眠的进程。虽然都要睡眠和交替,但是
select和poll在“醒着”的时候要遍历整个fd集合,而epoll在“醒着”的 时候只要判断一下就绪链表是否为空就行
了,这节省了大量的CPU时间。这就是回调机制带来的性能提升

(2)select,poll每次调用都要把fd集合从用户态往内核态拷贝一次,并且要把current往设备等待队列中挂一
次,而epoll只要 一次拷贝,而且把current往等待队列上挂也只挂一次(在epoll_wait的开始,注意这里的等
待队列并不是设备等待队列,只是一个epoll内 部定义的等待队列)。这也能节省不少的开销。

 

4. 平时用到哪些 Linux 命令。

递归计算当前目录的文件,包括隐藏文件。

# find . -type f | wc -l
7

语法:

find : 搜索目录结构中的文件

-type : 文件类型

f : 常规文件

-l : 输出换行符的数量

wc [选项] 文件 :该命令统计给定文件中的字节数、字数、行数。如果没有给出文件名,则从标准输入读取。wc同时也给出所有指定文件的总统计数。字是由空格字符区分开的最大字符串。

该命令各选项含义如下
- c 统计字节数。
- l 统计行数。
- w 统计字数。

$ wc - lcw file1 file2
4 33 file1
7 52 file2
11 11 85 total

5. 用一行命令查看文件的最后五行。

输出test文件的后五行:
 
liyi@liyi:~/Desktop > tail -n 5 test
 
输出test文件的前五行:
 
liyi@liyi:~/Desktop > head -n 5 test

 

6. 用一行命令输出正在运行的 java 进程。

 

7. 介绍下你理解的操作系统中线程切换过程。

 

8. 进程和线程的区别。

 

9.用户态和内核态是什么?

内核态: CPU可以访问内存所有数据, 包括外围设备, 例如硬盘, 网卡. CPU也可以将自己从一个程序切换到另一个程序

用户态: 只能受限的访问内存, 且不允许访问外围设备. 占用CPU的能力被剥夺, CPU资源可以被其他程序获取

 

多线程

1. 多线程的几种实现方式,什么是线程安全。

 

2. volatile 的原理,作用,能代替锁么。

 

3. 画一个线程的生命周期状态图。

 

4. sleep 和 wait 的区别。

 

5. Lock 与 Synchronized 的区别。

  总结来说,Lock和synchronized有以下几点不同:

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

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

  3)Lock可以让等待锁的线程响应中断,而synchronized却不行,使用synchronized时,等待的线程会一直
等待下去,不能够响应中断;

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

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

  在性能上来说,如果竞争资源不激烈,两者的性能是差不多的,而当竞争资源非常激烈时(即有大量线程同时
竞争),此时Lock的性能要远远优于synchronized。所以说,在具体使用时要根据适当情况选择。

6. synchronized 的原理是什么,解释以下名词:重排序,自旋锁,偏向锁,轻量级锁,可重入锁,公平锁,非公平锁,乐观锁,悲观锁。

同步方法通过ACC_SYNCHRONIZED关键字隐式的对方法进行加锁。当线程要执行的方法被标注上
ACC_SYNCHRONIZED时,需要先获得锁才能执行该方法。

同步代码块通过monitorenter和monitorexit执行来进行加锁。当线程执行到monitorenter的时候要先获得所
锁,才能执行后面的方法。当线程执行到monitorexit的时候则要释放锁。

每个对象自身维护这一个被加锁次数的计数器,当计数器数字为0时表示可以被任意线程获得锁。当计数器不为0
时,只有获得锁的线程才能再次获得锁。即可重入锁。

http://www.hollischuang.com/archives/1883

 

7. 用过哪些原子类,他们的原理是什么。

 

8. 用过线程池吗,newCache 和 newFixed 有什么区别,他们的原理简单概括下,构造函数的各个参数的含义是什么,比如 coreSize,maxsize 等。

Executors.newCachedThreadPool():无限线程池。

Executors.newFixedThreadPool(nThreads):创建固定大小的线程池。

Executors.newSingleThreadExecutor():创建单个线程的线程池。

1.ExecutorService threadPool = Executors.newCachedThreadPool();  
SynchronousQueue是一个没有数据缓冲的BlockingQueue,生产者线程对其的插入操作put必须等待消费者的移
除操作take。所以newCachedThreadPool实际项目一般也很少运用


2.ExecutorService executor = Executors.newFixedThreadPool(3); 
这个线程池的队列LinkedBlockingQueue没有指定默认大小,高并发环境下
在对内存压力很大,所以生产环境一般都不使用这个
	      

9. 线程池的关闭方式有几种,各自的区别是什么。

shutdown() 执行后停止接受新任务,会把队列的任务执行完毕。

shutdownNow() 也是停止接受新任务,但会中断所有的任务,将线程池状态变为 stop。

https://mp.weixin.qq.com/s/lpf2UKzxe9aS99oVIdc5pw

 

10. 假如有一个第三方接口,有很多个线程去调用获取数据,现在规定每秒钟最多有 10 个线程同时调用它,如何做到。

 

11. spring 的 controller 是单例还是多例,怎么保证并发的安全。

singleton : bean在每个Spring ioc 容器中只有一个实例。
prototype:一个bean的定义可以有多个实例。
request:每次http请求都会创建一个bean,该作用域仅在基于web的Spring ApplicationContext情形下有效。

 

12. 用三个线程按顺序循环打印 abc 三个字母,比如 abcabcabc。

public class PrintABC {  
	  
    public static Boolean isThreadA = true;  
    public static Boolean isThreadB = false;  
    public static Boolean isThreadC = false;  
  
    public static void main(String[] args) {  
        final PrintABC abc = new PrintABC();  
        new Thread(new Runnable() {  
            public void run() {  
                for (int i = 0; i < 10; i++) {  
                    synchronized (abc) {  
                        while(!isThreadA) {  
                            try {  
                                abc.wait();  
                            } catch (InterruptedException e) {  
                                // TODO Auto-generated catch block  
                                e.printStackTrace();  
                            }  
                        }  
                            System.out.print("A");  
                            isThreadA = false;  
                            isThreadB = true;  
                            isThreadC = false;  
                            abc.notifyAll();  
                    }  
                }  
            }  
        }).start();  
  
        new Thread(new Runnable() {  
            public void run() {  
                for (int i = 0; i < 10; i++) {  
                    synchronized (abc) {  
                        while(!isThreadB) {  
                            try {  
                                abc.wait();  
                            } catch (InterruptedException e) {  
                                // TODO Auto-generated catch block  
                                e.printStackTrace();  
                            }  
                        }  
                            System.out.print("B");  
                            isThreadA = false;  
                            isThreadB = false;  
                            isThreadC = true;  
                            abc.notifyAll();  
                    }  
                }  
            }  
        }).start();  
          
        new Thread(new Runnable() {  
            public void run() {  
                for (int i = 0; i < 10; i++) {  
                    synchronized (abc) {  
                        while(!isThreadC) {  
                            try {  
                                abc.wait();  
                            } catch (InterruptedException e) {  
                                // TODO Auto-generated catch block  
                                e.printStackTrace();  
                            }  
                        }  
                            System.out.print("C");  
                            isThreadA = true;  
                            isThreadB = false;  
                            isThreadC = false;  
                            abc.notifyAll();  
                    }  
                }  
            }  
        }).start();  
    }  
}

 

13. ThreadLocal 用过么,用途是什么,原理是什么,用的时候要注意什么。

 

14. 如果让你实现一个并发安全的链表,你会怎么做。

http://blog.csdn.net/iter_zc/article/details/41115021

 

15. 有哪些无锁数据结构,他们实现的原理是什么。

ConcurrentLinkedQueue
从源代码角度来看整个入队过程主要做二件事情。第一是定位出尾节点,第二是使用CAS算法能将入队节点设置成
尾节点的next节点,如不成功则重试。

http://blog.csdn.net/iter_zc/article/details/4111502

16. 讲讲 java 同步机制的 wait 和 notify。

 

17. 多线程如果线程挂住了怎么办。

 

18. countdowlatch 和 cyclicbarrier的内部原理和用法,以及相互之间的差别。

1.CountDownLatch减计数,CyclicBarrier加计数。 
2.CountDownLatch是一次性的,CyclicBarrier可以重用。

应用场景
对于CountDownLatch来说,重点是那个“一个线程”, 是它在等待, 而另外那N的线程在把“某个事情”做完之后可以继续等待,可以终止。
对于CyclicBarrier来说,重点是那N个线程,他们之间任何一个没有完成,所有的线程都必须等待。

19. 使用 synchronized 修饰静态方法和非静态方法有什么区别。

 所有的非静态同步方法用的都是同一把锁——实例对象本身,也就是说如果一个实例对象的非静态同步方法获取锁后,
  该实例对象的其他非静态同步方法必须等待获取锁的方法释放锁后才能获取锁,
  可是别的实例对象的非静态同步方法因为跟该实例对象的非静态同步方法用的是不同的锁,
  所以毋须等待该实例对象已获取锁的非静态同步方法释放锁就可以获取他们自己的锁。
 
  而所有的静态同步方法用的也是同一把锁——类对象本身,这两把锁是两个不同的对象,
  所以静态同步方法与非静态同步方法之间是不会有竞态条件的。但是一旦一个静态同步方法获取锁后,
  其他的静态同步方法都必须等待该方法释放锁后才能获取锁,而不管是同一个实例对象的静态同步方法之间,
  还是不同的实例对象的静态同步方法之间,只要它们同一个类的实例对象!

 

20. 简述 ConcurrentLinkedQueue 和 LinkedBlockingQueue 的用处和不同之处。

LinkedBlockingQueue 一个由链接节点支持的可选有界阻塞队列。

ConcurrentLinkedQueue 通过compare and swap(简称CAS)协议的方式,来保证多线程情况下数据的安全,不加锁,主要使用了Java中的要使用了Java中的sun.misc.Unsafe类来实现

21. 导致线程死锁的原因?怎么解除线程死锁。

一、导致线程死锁的原因

两个不同的线程加锁的顺序不一样

二、怎么解除线程死锁

1.加锁过期时限,当一个线程持有锁的时间达到一定值时,wait释放锁,等待一段随机的时间再重试

2.当几个线程都要访问共享资源A、B、C时,保证使每个线程都按照同样的顺序去访问它们,比如都先访问A,在访问B和C。 

22. 非常多个线程(可能是不同机器),相互之间需要等待协调,才能完成某种工作,问怎么设计这种协调方案。

CountDownLatch,CyclicBarrier

 

23. 正确使用 Volatile 变量

正确使用 volatile 变量的条件
 
您只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
 
1对变量的写操作不依赖于当前值。
2该变量没有包含在具有其他变量的不变式中。

https://blog.csdn.net/lzhcoder/article/details/84726583

https://blog.csdn.net/lzhcoder/article/details/53197744

 

24. BIO,NIO与AIO的区别

Java BIO :在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个
ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线
程等待请求,而客户端发送请求后,先咨询服务端是否有线程相应,如果没有则会一直等待或者遭到拒绝请求,
如果有的话,客户端会线程会等待请求结束后才继续执行。
 

Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用
器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

Java AIO(NIO.2) : 异步非阻塞,NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可,
这两种方法均为异步的。
 

NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复
杂,JDK1.4开始支持。

AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比
较复杂,JDK7开始支持

http://www.cnblogs.com/barrywxx/p/8430790.html

 

25.synchronized和reentrantlock异同

相同点

都实现了多线程同步和内存可见性语义

都是可重入锁

不同点

实现机制不同 synchronized通过java对象头锁标记和Monitor对象实现 reentrantlock通过CAS、
ASQ(AbstractQueuedSynchronizer)和locksupport(用于阻塞和解除阻塞)实现 synchronized依赖jvm内
存模型保证包含共享变量的多线程内存可见性 reentrantlock通过ASQ的volatile state保证包含共享变量的多
线程内存可见性

使用方式不同 synchronized可以修饰实例方法(锁住实例对象)、静态方法(锁住类对象)、代码块(显示指定
锁对象) reentrantlock显示调用trylock()/lock()方法,需要在finally块中释放锁

功能丰富程度不同 reentrantlock提供有限时间等候锁(设置过期时间)、可中断锁(lockInterruptibly)、
condition(提供await、signal等方法)等丰富语义 reentrantlock提供公平锁和非公平锁实现 
synchronized是非公平锁,不可设置等待时间、不可被中断(interrupted), 

26.公平锁和非公平锁 

公平锁:

获取不到锁的时候,会自动加入队列,等待线程释放后,队列的第一个线程获取锁

非公平锁:

获取不到锁的时候,会自动加入队列,等待线程释放锁后所有等待的线程同时去竞争

27.为什么Java中 wait 方法需要在 synchronized 的方法中调用?

1) Java 会抛出 IllegalMonitorStateException,如果我们不调用来自同步上下文的wait(),notify()或者notifyAll()方法。

2) 如果我们不在同步方法或块中调用它们就可能存在wait()和 notify()之间的竞态条件。由于竞态条件,没有获取到锁,我们可能会丢失通知,如果我们使用缓冲区或只使用一个元素,生产线程将永远等待,你的程序将挂起

TCP  HTTP

1. http/1.0、http/1.1和http2.0有什么区别

在http1.0中,当建立连接后,客户端发送一个请求,服务器端返回一个信息后就关闭连接,
当浏览器下次请求的时候又要建立连接,显然这种不断建立连接的方式,会造成很多问题。
   
在http1.1中,引入了持续连接的概念,通过这种连接,浏览器可以建立一个连接之后,
发送请求并得到返回信息,然后继续发送请求再次等到返回信息,也就是说客户端可以连续发送多个请求,而不用等待每一个响应的到来。

在http/2.0中,支持多路复用技术,同一个连接并发处理多个请求(NIO),http/1.1可以通过建立多个TCP解决

 

2. TCP 三次握手和四次挥手的流程,为什么断开连接要 4次,如果握手只有两次,会出现什么。

TCP看似复杂,其实可以归纳为以下5种报文:
(1)SYN
(2)Data (唯一携带用户数据)
(3)FIN
(4)Reset
(5)ACK

  • 其中1、2、3分别为建立连接、数据传输、断开连接,这三种报文对方接收到一定要ACK确认,为何要确认,因为这就是可靠传输的依赖的机制。如果对方在超时时间内不确认,发送方会一直重传,直到对方确认为止、或到达重传上限次数而Reset连接。
  • 4、5 为重置连接报文、确认ACK报文,这两种报文对方接收到要ACK确认吧?不需要!自然发送方也不会重传这2种类型的报文。

为何Reset报文不需要ACK确认?

因为发送Reset报文的一端,在发送完这个报文之后,和该TCP Session有关的内存结构体瞬间全部释放,无论对方收到或没有收到,关系并不大。

  • 如果对方收到Reset报文,也会释放该TCP Session 的相关内存结构体。
  • 如果对方没有收到Reset 报文,可能会继续发送让接收方弹射出Reset报文的报文,到最后对方一样会收到Reset 报文,并最终释放内存。

为何ACK报文不需要ACK确认?

这里的ACK报文,是指没有携带任何数据的裸ACK报文,对方收到这样的ACK报文,自然也不需要ACK。
否则,对方为了ACK己方的ACK,那己方收到对方的ACK,也要ACK对方的ACK,这就是一个死循环,永无止息。所以为了避免这个死循环,一律不允许ACK对方的裸ACK报文。

为什么TCP建立连接不是四次握手?

  1. A 发送SYN 报文给B,这是第一次报文交互。
  2. B发送ACK确认A的SYN报文,这是第二次报文交互
  3. B发送自己的SYN报文给A,这是第三次报文交互
  4. A需要ACK确认B的SYN报文,这是第四次报文交互

以上的演绎没有问题,但是报文2、3为何要分开发送呢?增加了延迟不说,同时还白白浪费了网络的带宽,完全可以将报文2、3合并起来,不就是在报文2的ACK状态位的位置置“1”就结了吗?

一句话概括,TCP连接握手,握的是啥?

通信双方数据原点的序列号!

TCP作为一种可靠传输控制协议,其核心思想:既要保证数据可靠传输,又要提高传输的效率,而用三次恰恰可以满足以上两方面的需求!
TCP可靠传输的精髓:TCP连接的一方A,由操作系统动态随机选取一个32位长的序列号(Initial Sequence Number)

 

什么TCP建立连接不是两次握手?

  • 因为双方都需要确认对方的起始序号! 这里必须需要两次握手了!
  • 还有一次发起同步请求.

 

TCP连接断开过程:
1、客户端TCP模块在收到应用程序的通知后,发送FIN,seq=x。
2、服务器收到FIN报文段,发送ACK,确认号=x+1,并且通知应用程序客户端关闭了连接。客户端收到ACK报文段。
3、服务器端的应用程序通知TCP关闭连接,服务器端TCP发送FIN+ACK,seq=y,确认号=x+1(此时服务端已经断开连接)。
4、客户端收到FIN+ACK报文段后,发送ACK,确认号y+1。服务器收到ACK报文段后,连接断开。

为什么不把第二步,第三步合在一起进行呢?
因为,第二步中,服务器端通知应用程序并获得反馈信息可能需要可观的时间,这可能涉及人机交互操作,也可能服务器应用层暂时还不想关闭连接。第二步结束后,服务器还可以继续通过这条连接发送数据给客户端,客户端已经不能发送数据了,但是仍然可以回复ACK。第二步中服务器立即发送确认是为了防止客户端重传FIN报文

 

https://blog.csdn.net/jxzdsw/article/details/82759419

https://mp.weixin.qq.com/s/--8On2ejsFKy_HkmcDjEIQ

3. TIME_WAIT 和 CLOSE_WAIT 的区别。

CLOSE_WAIT:等待关闭,是被动关闭连接形成的,也就是第二次挥手时产生的状态。也就是当对方close一个
SOCKET后发送FIN报文给自己,系统会回应一个ACK报文给对方,此时进入CLOSE_WAIT状态。接着,我们需要考虑
的事情是查看是否还有数据发送给对方,如果没有就可以close这个链接,发送FIN给对方,也既关闭连接。所以
在CLOSE_WAIT状态时,需要查看自己是否需要关闭连接。

TIME_WAIT:是主动关闭连接方形成的,表示收到了对方的FIN报文,并发送ACK报文,等待
2MSL(Maximum Segment Lifetime:报文最大生存时间)约4分钟时间后进入CLOSE状态。主要是防止最后一个
ACK丢失,由于TIME_WAIT等待时间较长,因此server端尽量减少关闭。

4. 说说你知道的几种 HTTP 响应码,比如 200, 302, 404。

200 OK:表示客户端请求成功。
301 redirect: 301 代表永久性转移(Permanently Moved)
302 redirect: 302 代表暂时性转移(Temporarily Moved )
400 Bad Request 语义有误,不能被当前服务器理解。
401 Unauthorized 当前请求需要用户验证。
403 Forbidden 服务器收到消息,但是拒绝提供服务。
404 Not Found 请求资源不存在。
408 Request Timeout 请求超时,客户端没有在服务器预备等待的时间内完成发送。
500 Internal Server Error 服务器发生不可预期的错误。
503 Server Unavailable 由于临时的服务器维护或过载,服务器当前不能处理请求,此状况知识临时的,可恢
复

5. 当你用浏览器打开一个链接的时候,计算机做了哪些工作步骤。

域名解析--> 发起TCP的3次握手 --> 建立TCP连接后发起http请求 --> 服务器响应http请求--> 浏览器得到html代码 --> 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等) --> 浏览器对页面进行渲染呈现给用户

6. TCP/IP 如何保证可靠性,说说 TCP 头的结构。

 

7. 如何避免浏览器缓存。

无法被浏览器缓存的请求:
HTTP信息头中包含Cache-Control:no-cache,pragma:no-cache,或Cache-Control:max-age=0等告诉浏览器不用缓存的请求
需要根据Cookie,认证信息等决定输入内容的动态请求是不能被缓存的
经过HTTPS安全加密的请求(有人也经过测试发现,ie其实在头部加入Cache-Control:max-age信息,firefox在头部加入Cache-Control:Public之后,能够对HTTPS的资源进行缓存,参考《HTTPS的七个误解》)
POST请求无法被缓存
HTTP响应头中不包含Last-Modified/Etag,也不包含Cache-Control/Expires的请求无法被缓存

8. 简述 Http 请求 get 和 post 的区别以及数据包格式。

GET提交,请求的数据会附在URL之后(就是把数据放置在HTTP协议头<request-line>中
POST提交:把提交的数据放置在是HTTP包的包体<request-body>中

9. 简述 HTTP 请求的报文格式。

一个HTTP请求报文由请求行(request line)、请求头部(header)、空行和请求数据4个部分组成

http://blog.csdn.net/zhangliang_571/article/details/23508953

10. HTTPS 的加密方式是什么,讲讲整个加密解密流程。

  HTTPS其实是有两部分组成:HTTP + SSL / TLS,也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传输都会通过TLS进行加密,所以传输的数据都是加密后的数据 

1. 客户端发起HTTPS请求

2. 服务端的配置

        采用HTTPS协议的服务器必须要有一套数字证书,可以自己制作,也可以向组织申请

3. 传送证书

       这个证书其实就是公钥,只是包含了很多信息,如证书的颁发机构,过期时间等等。

4. 客户端解析证书

  这部分工作是有客户端的TLS来完成的,首先会验证公钥是否有效,比如颁发机构,过期时间等等,如果发现异常,则会弹出一个警告框,提示证书存在问题。如果证书没有问题,那么就生成一个随机值(私匙)。然后用证书对该随机值进行加密。就好像上面说的,把随机值用锁头锁起来,这样除非有钥匙,不然看不到被锁住的内容。

5. 传送加密信息

  这部分传送的是用证书加密后的随机值,目的就是让服务端得到这个随机值,以后客户端和服务端的通信就可以通过这个随机值来进行加密解密了。

6. 服务段解密信息

  服务端用私钥解密后,得到了客户端传过来的随机值(私钥),然后把内容通过该值进行对称加密。所谓对称加密就是,将信息和私钥通过某种算法混合在一起,这样除非知道私钥,不然无法获取内容,而正好客户端和服务端都知道这个私钥,所以只要加密算法够彪悍,私钥够复杂,数据就够安全。

7. 传输加密后的信息

  这部分信息是服务端用私钥加密后的信息,可以在客户端被还原。

8. 客户端解密信息

  客户端用之前生成的私钥解密服务端传过来的信息,于是获取了解密后的内容。整个过程第三方即使监听到了数据,也

11.对称加密和非非对称加密

Java中高级面试题总览_第1张图片

12.TCP与UDP的区别是什么 

1.基于连接与无连接;
2.对系统资源的要求(TCP较多,UDP少);
3.UDP程序结构较简单;
4.流模式与数据报模式 ;
5.TCP保证数据正确性,UDP可能丢包,TCP保证数据顺序,UDP不保证。

UDP应用场景:
1.面向数据报方式
2.网络数据大多为短消息 
3.拥有大量Client
4.对数据安全性无特殊要求
5.网络负担非常重,但对响应速度要求高

 

架构设计与分布式

1. 常见的缓存策略有哪些,你们项目中用到了什么缓存系统,如何设计的。

 

2. 用 java 自己实现一个 LRU。

 

3. 分布式集群下如何做到唯一序列号。

 

 

4. 设计一个秒杀系统,30 分钟没付款就自动关闭交易。

https://blog.csdn.net/lzhcoder/article/details/87883356

5. 如何使用 redis 和 zookeeper 实现分布式锁?有什么区别优缺点,分别适用什么场景。

Redis 的分布式锁而言,它有以下缺点:

它获取锁的方式简单粗暴,获取不到锁直接不断尝试获取锁,比较消耗性能

另外来说的话,Redis 的设计定位决定了它的数据并不是强一致性的,在某些极端情况下,可能会出现问题。锁的模型不够健壮

 

对于 ZK 分布式锁而言:

ZK 天生设计定位就是分布式协调,强一致性。锁的模型健壮、简单易用、适合做分布式锁。

如果获取不到锁,只需要添加一个监听器就可以了,不用一直轮询,性能消耗较小。

但是 ZK 也有其缺点:如果有较多的客户端频繁的申请加锁、释放锁,对于 ZK 集群的压力会比较大

6. 如果有人恶意创建非法连接,怎么解决。

 

7. 分布式事务的原理,优缺点,如何使用分布式事务。

 

8. 什么是一致性 hash。

假设:我们增加了一台缓存服务器,那么缓存服务器的数量就由4台变成了5台。那么原本hash(a.png) % 4 = 2 的公式就变成了hash(a.png) % 5 = ? , 可想而知这个结果肯定不是2的,这种情况带来的结果就是当服务器数量变动时,所有缓存的位置都要发生改变!换句话说,当服务器数量发生改变时,所有缓存在一定时间内是失效的,当应用无法从缓存中获取数据时,则会向后端数据库请求数据。

一致性Hash算法也是使用取模的方法,只是,刚才描述的取模法是对服务器的数量进行取模,而一致性Hash算法是对2^32取模。一致性Hash算法对于节点的增减都只需重定位环空间中的一小部分数据,具有较好的容错性和可扩展性。

9. 什么是 restful,讲讲你理解的 restful。

 

10. 如何设计建立和保持 100w 的长连接。

https://blog.csdn.net/lzhcoder/article/details/84937588

 

11. 如何防止缓存雪崩。

 

12. 解释什么是 MESI 协议(缓存一致性)。

 

13. 说说你知道的几种 HASH 算法,简单的也可以。

 

14. 什么是 paxos 算法。

 

15. 什么是 zab 协议。

整个ZAB协议主要包括消息广播和崩溃恢复两个过程,进一步可以分为三个阶段,分别是:

发现 Discovery
同步 Synchronization
广播 Broadcast
组成ZAB协议的每一个分布式进程,都会循环执行这三个阶段,将这样一个循环称为一个主进程周期。

16. 一个在线文档系统,文档可以被编辑,如何防止多人同时对同一份文档进行编辑更新。

 

17. 线上系统突然变得异常缓慢,你如何查找问题。

18. 说说你平时用到的设计模式。 

19. Dubbo 的原理,数据怎么流转的,怎么实现集群,负载均衡,服务注册和发现。重试转发,快速失败的策略是怎样的。

 

20. 一次 RPC 请求的流程是什么。

 

21. 异步模式的用途和意义。

 

22. 缓存数据过期后的更新如何设计。

 

23. 编程中自己都怎么考虑一些设计原则的,比如开闭原则,以及在工作中的应用。

 

24. 设计一个社交网站中的“私信”功能,要求高并发、可扩展等等。画一下架构图。

 

25. MVC 模式,即常见的 MVC 框架。

 

26. 聊了下曾经参与设计的服务器架构。

 

27. 应用服务器怎么监控性能,各种方式的区别。

 

28. 如何设计一套高并发支付方案,架构如何设计。

https://blog.csdn.net/lzhcoder/article/details/87883356

 

29. 如何实现负载均衡,有哪些算法可以实现。

 

30. Zookeeper 的用途,选举的原理是什么。

 

31. Mybatis 的底层实现原理。

MyBatis底层就是JDBC   所以他的核心就是配置文件  :
 
      1:全局配置文件 (配置数据源 事务运行时信息)
      2:映射文件(执行statement的相关信息,包括SQL语句,输入参数,输出结果)
      MyBatis把全局配置文件加载到内容中 构建出SqlSessionFactory    ,这个工厂的作用相当于生产对象
生产SqlSession。
     SqlSession   :它是一个面向程序员的接口,可以操作数据库。 接口有一个默认实现DefaultSqlSession。
在SqlSession   中有一个executor 执行器。  SqlSession   本身不能操作数据库 需要通过这个执行器去操
作。有2个实现 一个叫做基本执行器,还有一个缓存执行器
(默认)。 MappedStatement:封装了执行Statement信息,包括SQL语句 输入参数,输出结果。由它去操作数
据库。
 
输入输出参数类型:
            1:基本类型
            2:自定义类型
            3:hashmap
 
根据源码:看到Sqlsession内部并不能直接操作数据库。而是利用内部的一个执行器去操作数据库。执行器执行
的时候会去执行MappedStatement 
到最后才去真正执行数据库。

 

32. 请思考一个方案,设计一个可以控制缓存总体大小的自动适应的本地缓存。

 

33. 请思考一个方案,实现分布式环境下的 countDownLatch。

 

34. 后台系统怎么防止请求重复提交。

用隐藏表单记录token属性(注意隐藏域中的名字必须与下面令牌类定义的静态变量TOKEN_KEY一致)。当提交表
单后,HandlerServlet程序会比较隐藏域中的值和Session域中的标志号,如果相同就处理表单数据,处理后就
清除当前用户Session域中存储的标识符,如果不相同就会忽略表单请求。当重复提交表单时,当前Session域中
会不存在相应的表单标识号

http://blog.csdn.net/wws199304/article/details/44279589

 

35. 如何看待缓存的使用(本地缓存,集中式缓存),简述本地缓存和集中式缓存和优缺点。本地缓存在并发使用时的注意事项。

 

36. 描述一个服务从发布到被消费的详细过程。

 

37. 讲讲你理解的服务治理。

 

38. 如何做到接口的幂等性。

接口幂等性,只要保证接口内的逻辑不涉及接口外的对象状态累积或变迁即可。

譬如说需求是:
当用户点击赞同时,将答案的赞同数量+1。
改为:
当用户点击赞同时,确保答案赞同表中存在一条记录,用户、答案。
赞同数量由答案赞同表统计出来

 

39.集群部署时的分布式session如何实现?

(1)tomcat + redis

这个其实还挺方便的,就是使用session的代码跟以前一样,还是基于tomcat原生的session支持即可,然后
就是用一个叫做Tomcat RedisSessionManager的东西,让所有我们部署的tomcat都将session数据存储到redis即可。

在tomcat的配置文件中,配置一下





搞一个类似上面的配置即可,你看是不是就是用了RedisSessionManager,然后指定了redis的host和 port就ok了。




还可以用上面这种方式基于redis哨兵支持的redis高可用集群来保存session数据,都是ok的

(2)spring session + redis

分布式会话的这个东西重耦合在tomcat中,如果我要将web容器迁移成jetty,难道你重新把jetty都配置一遍
吗?

因为上面那种tomcat + redis的方式好用,但是会严重依赖于web容器,不好将代码移植到其他web容器上
去,尤其是你要是换了技术栈咋整?比如换成了spring cloud或者是spring boot之类的。还得好好思忖思
忖。

所以现在比较好的还是基于java一站式解决方案,spring了。人家spring基本上包掉了大部分的我们需要使用
的框架了,spirng cloud做微服务了,spring boot做脚手架了,所以用sping session是一个很好的选择。

pom.xml


  org.springframework.session
  spring-session-data-redis
  1.2.1.RELEASE


  redis.clients
  jedis
  2.8.1


spring配置文件中


    



    
    



    
    
    
    
    
    


web.xml


    springSessionRepositoryFilter
    org.springframework.web.filter.DelegatingFilterProxy


    springSessionRepositoryFilter
    /*


示例代码

@Controller
@RequestMapping("/test")
public class TestController {

@RequestMapping("/putIntoSession")
@ResponseBody
    public String putIntoSession(HttpServletRequest request, String username){
        request.getSession().setAttribute("name",  “leo”);

        return "ok";
    }

@RequestMapping("/getFromSession")
@ResponseBody
    public String getFromSession(HttpServletRequest request, Model model){
        String name = request.getSession().getAttribute("name");
        return name;
    }
}

上面的代码就是ok的,给sping session配置基于redis来存储session数据,然后配置了一个spring 
session的过滤器,这样的话,session相关操作都会交给spring session来管了。接着在代码中,就用原生
的session操作,就是直接基于spring sesion从redis中获取数据了。

40.如果让你设计一个消息中间件,你会怎么做?

1.生产消费模型以及核心数据结构
可以先允许数据写入内存作为一个缓冲,然后每隔几秒再把数据刷入磁盘文件中,数据写入磁盘文件之后,是不是
要有相应的一些metadata来标识这个数据的offset偏移量,和内置的唯一id。一个queue里的数据,是会均匀分
配给消费者的各个实例

算法

1. 10 亿个数字里里面找最小的 10 个。

内部排序
先用前k个元素生成一个小顶堆,这个小顶堆用于存储,当前最大的k个元素。
接着,从第k+1个元素开始扫描,和堆顶(堆中最小的元素)比较,如果被扫描的元素大于堆顶,则替换堆顶的元
素,并调整堆,以保证堆内的k个元素,总是当前最大的k个元素。
直到,扫描完所有n-k个元素,最终堆中的k个元素,就是猥琐求的TopK。

外部排序
先根据数据值或者把数据hash(MD5)后的值按照范围划分到不同的机器上,最好可以让数据划分后一次读入内存这
样不同的机器负责处理不同的数值范围,实际上就是Map。得到结果后,各个机器只需拿出各自出现次数最多的前
个数据,然后汇总,选出所有的数据中出现次数最多的前N个数据,这实际上就是Reduce过程

2. 有 1 亿个数字,其中有 2 个是重复的,快速找到它,时间和空间要最优。

 

3. 2 亿个随机生成的无序整数,找出中间大小的值。

 

4. 给一个不知道长度的(可能很大)输入字符串,设计一种方案,将重复的字符排重。

 

5. 遍历二叉树。

 

6. 有 3n+1 个数字,其中 3n 个中是重复的,只有 1 个是不重复的,怎么找出来。

 

7. 写一个字符串反转函数。

 

8. 常用的排序算法,快排,归并、冒泡。 快排的最优时间复杂度,最差复杂度。冒泡排序的优化方案。

 

排序法

最差时间分析 平均时间复杂度 稳定度 空间复杂度
冒泡排序 O(n2) O(n2) 稳定 O(1)
快速排序 O(n2) O(n*log2n) 不稳定 O(log2n)~O(n)
选择排序 O(n2) O(n2) 不稳定 O(1)
二叉树排序 O(n2) O(n*log2n) 不一顶 O(n)

插入排序

O(n2) O(n2) 稳定 O(1)
堆排序 O(n*log2n) O(n*log2n) 不稳定 O(1)
归并排序 O(n*log2n) O(n*log2n) 稳定 O(n)
public void sortArray(int[] array){
    int temp;
    for(int i=0; i array[j+1]){
                temp = array[j];
                array[j] = array[j+1];
                array[j+1] = temp;
                isSorted = false;
            }
        }
        if(isSorted){
            break;
        }
    }
}

 

定义了一个boolean类型的isSorted变量,用来判断往后的循环当中,数组是否已经是有序的,每一轮循环都会设置其值为true,当有元素对调位置时,就将isSorted的值设置为false,表示该数组还不是有序数组。每一轮都要判断isSorted的值,如果判断当前一轮操作没有元素有位置调换,那么可以提前结束所有的循环 

9. 二分查找的时间复杂度,优势。

 

10. 一个已经构建好的 TreeSet,怎么完成倒排序。

 

11.md5加密的原理

MD5是输入不定长度信息,输出固定长度128-bits的算法。经过程序流程,生成四个32位数据,最后联合起来成为
一个128-bits散列。基本方式为,求余、取余、调整长度、与链接变量进行循环运算。得出结果。

https://www.cnblogs.com/ttss/p/4243274.html

 

12.给一台普通PC,2G内存,要求处理一个包含40亿个不重复并且没有排过序的无符号的int整数,给出一个整数,问如果快速地判断这个整数是否在文件40亿个数据当中?

问题思考:
40亿个int占(40亿*4)/1024/1024/1024 大概为14.9G左右,很明显内存只有2G,放不下,因此不可能将这
40亿数据放到内存中计算。要快速的解决这个问题最好的方案就是将数据搁内存了,所以现在的问题就在如何
在2G内存空间以内存储着40亿整数。一个int整数在java中是占4个字节的即要32bit位,如果能够用一个bit
位来标识一个int整数那么存储空间将大大减少,算一下40亿个int需要的内存空间为40亿/8/1024/1024大概
为476.83 mb,这样的话我们完全可以将这40亿个int数放到内存中进行处理。

具体思路:

   1个int占4字节即4*8=32位,那么我们只需要申请一个int数组长度为 int tmp[1+N/32]即可存储完这些
数据,其中N代表要进行查找的总数,tmp中的每个元素在内存在占32位可以对应表示十进制数0~31,所以可得
到BitMap表:

tmp[0]:可表示0~31

tmp[1]:可表示32~63

tmp[2]:可表示64~95

.......

那么接下来就看看十进制数如何转换为对应的bit位:

假设这40亿int数据为:6,3,8,32,36,......,那么具体的BitMap表示为:

00000000000000000000000101001001
00000000000000000000000000010000

如何判断int数字在tmp数组的哪个下标,这个其实可以通过直接除以32取整数部分,例如:整数8除以32取整
等于0,那么8就在tmp[0]上。另外,我们如何知道了8在tmp[0]中的32个位中的哪个位,这种情况直接mod上
32就ok,又如整数8,在tmp[0]中的第8 mod上32等于8,那么整数8就在tmp[0]中的第八个bit位(从右边数
起)。


解法二:
Bloom Filter 原理

下面来分析下它的实现原理。

官方的说法是:它是一个保存了很长的二进制向量,同时结合 Hash 函数实现的。


对写入的数据做 H 次 hash 运算定位到数组中的位置,同时将数据改为 1 。当有数据查询时也是同样的方式定
位到数组中。 一旦其中的有一位为 0 则认为数据肯定不存在于集合,否则数据可能存在于集合中。

布隆过滤有以下几个特点:
只要返回数据不存在,则肯定不存在。
返回数据存在,但只能是大概率存在。
同时不能清除其中的数据。

它不适合那些"零误判"的应用场合.在能容忍低误判的应用场景下,布隆过滤器通过极少的误判换区了存储空间的极大节省.

https://mp.weixin.qq.com/s/S1jp0O4fM8r4qOpOQLGQIw

 

13. 在2.5亿个整数中找出不重复的整数,注,内存不足以容纳这2.5亿个整数

解法一:将bit-map扩展一下,采用2-Bitmap(每个数分配2bit,00表示不存在,01表示出现一次,10表示多
次,11无意义)进行,共需内存2^32 * 2 bit=1 GB内存,还可以接受。然后扫描这2.5亿个整数,查看Bitmap
中相对应位,如果是00变01,01变10,10保持不变。所描完事后,查看bitmap,把对应位是01的整数输出即可。



或者我们不用2bit来进行表示,我们用两个bit-map即可模拟实现这个2bit-map,都是一样的道理。

解法二:根据数字hash,划分小文件。然后在小文件中找出不重复的整数,并排序。然后再进行归并,注意去除重复的元素。

https://blog.csdn.net/pipisorry/article/details/62443757

 

Mysql

1. 行转列

姓名       课程       分数

---------- ---------- -----------

张三       语文        74

张三       数学        83

张三       物理        93

李四       语文        74

李四       数学        84

李四       物理        94

SELECT 姓名,
 max(CASE 课程 WHEN'语文' THEN 分数 ELSE 0 END) 语文,
 max(CASE 课程 WHEN'数学' THEN 分数 ELSE 0 END) 数学,
 max(CASE 课程 WHEN'物理' THEN 分数 ELSE 0 END) 物理
FROM tb
GROUP BY 姓名
 
姓名        语文        数学        物理
---------- ----------- ----------- -----------
李四        74          84          94
张三        74          83          93

2. MySQL存储引擎- MyISAM与InnoDB区别

第一个重大区别是:InnoDB的主索引的数据文件本身就是索引文件。从上文知道,MyISAM主索引的索引文件和数据
文件是分离的,索引文件仅保存数据记录的地址。而在InnoDB中,表数据文件本身就是按B+Tree组织的一个索引
结(index=data),这棵树的叶节点data域保存了完整的数据记录。这个索引的key是数据表的主键,因此InnoDB
表数据文件本身就是
主索引。


第二个重大区别是:MyISAM类型不支持事务处理等高级处理,而InnoDB类型支持事务处理与外键和行级锁。MyISAM
类型的表强调的是性能,其执行速度比InnoDB类型更快,但是不提供事务支持,而InnoDB提供事务支持以及外部
键等高级数据库功能。 所以MyISAM往往就容易被人认为只适合在小项目中使用


第三个区别是:InnoDB的辅助索引data域存储相应记录主键的值而不是地址。而MyISAM的辅助索引和主索引没
有多大区别

    不同存储引擎的索引实现方式对于正确使用和优化索引都非常有帮助,例如知道了InnoDB的索引实现后,就很容易明白为什么不建议使用过长的字段作为主键,因为所有辅助索引都引用主索引,过长的主索引会令辅助索引变得过大。再例如,用非单调的字段作为主键在InnoDB中不是个好主意,因为InnoDB数据文件本身是一颗B+Tree,非单调的主键会造成在插入新记录时数据文件为了维持B+Tree的特性而频繁的分裂调整,十分低效,而使用自增字段作为主键则是一个很好的选择。 

3,数据库隔离级别有哪些,各自的含义是什么,Mysql默认的隔离级别是是什么

InnoDB使用多版本并发控制来获得高并发性,并且实现了sql标准的4种隔离级别,分别是:
1.未提交读:在未提交读级别,事务中的修改,即使没有提交,对其他事务也都是可见的

2.提交读(Read committed):大多数数据库系统的默认隔离级别都是提交读(但Mysql不是)一个事务开始
时,只能“看见”已经提交的事务所做的修改",该隔离级别有时也叫"不可重复度"

3.可重复读:可重复读解决了脏读的问题。该级别保证了在同一个事务中多次读取同样记录的结果是一致的。但是
理论上,可重复读隔离级别还是无法解决当某个事务在读取某个范围内的记录时,另外一个事务中又在该范围插入
了新的记录,当之前的事务再次读取该范围的记录时,会产生幻行。可重复读是MySQL的默认事务隔离级别。

4.可串行化:可串行化是最高的隔离级别。它通过强制事务串行执行,避免了前面所说的幻读问题。简单来说,可
串行化会在读取的每一行数据上都加上锁,所以可能导致大量的超时和锁争用问题


4.高并发下,如何做到安全的修改同一行数据,乐观锁和悲观锁是什么,INNODB的行级锁有哪2种,解释其含义

InnoDB实现了以下两种类型的行锁。

共享锁(S):允许一个事务去读一行,阻止同一Session其他事务获得相同数据集的排他锁。当事务同时增加共享锁时候,事务的更新必须等待先执行的事务 commit 后才行,如果同时并发太大可能很容易造成死锁

排他锁(X):允许获得排他锁的事务更新数据,阻止同一Session其他事务取得相同数据集的共享读锁和排他写
锁。

共享锁(S):SELECT * FROM table_name WHERE ... LOCK IN SHARE MODE。
排他锁(X):SELECT * FROM table_name WHERE ... FOR UPDATE


另外,为了允许行锁和表锁共存,实现多粒度锁机制,InnoDB还有两种内部使用的意向锁(Intention 
Locks),这两种意向锁都是表锁。
意向共享锁(IS):事务打算给数据行加行共享锁,事务在给一个数据行加共享锁前必须先取得该表的IS锁。
意向排他锁(IX):事务打算给数据行加行排他锁,事务在给一个数据行加排他锁前必须先取得该表的IX锁


 


InnoDB行锁是通过给索引上的索引项加锁来实现的,这一点与Oracle不同,后者是通过在数据块中对相应数据行
加锁来实现的。InnoDB这种行锁实现特点意味着:只有通过索引条件检索数据,InnoDB才使用行级锁,否则,
InnoDB将使用表锁!
在实际应用中,要特别注意InnoDB行锁的这一特性,不然的话,可能导致大量的锁冲突,从而影响并发性能


锁的算法
Record Lock :单个记录上的锁。
Gap Lock:间隙锁,锁定一个范围,但不包含记录本身。
Next-key Lock: 锁定一个范围和本身 Record Lock + Gap Lock。
例如一个索引有10,11,13,20这4个值,那么索引可能被Next-key Locking的区间为:
(- ∞,10), [10,11) , [11,13), [13,20), [20,+ ∞)
Next-key Lock为了解决幻读问题


5.SQL优化的一般步骤是什么,怎么看执行计划,如何理解其中各个字段的含义,索引的原理?

 

6.数据库会死锁吗,举一个死锁的例子,mysql怎么解决死锁

事务A等待事务B,同时事务B等待事务A,会产生死锁。
Innodb有死锁检测进程,如果检测到死锁,会马上抛出异常并回滚一个事务(另一个继续执行)

死锁的关键在于:两个(或以上)的Session加锁的顺序不一致。
那么对应的解决死锁问题的关键就是:让不同的session加锁有次序

 

7.什么是 B+树,B-树,列出实际的使用场景。

在讲B+树之前必须先了解二叉查找树、平衡二叉树(AVLTree)和多路平衡查找树(B-Tree),B+树即由这些
树逐步优化而来。

B+Tree相对于B-Tree有几点不同:
1.B+非叶节点不保存数据相关信息, 只保存关键字和子节点的引用
2.B+叶子节点是顺序排列的, 并且相邻节点具有顺序引用的关系(存在一个链指针)
3.B+关键字对应的数据保存在叶子节点中
4.B+节点关键字搜索采用左闭合区间(a<= x < b)


为什么使用B+Tree
红黑树等数据结构也可以用来实现索引,但是文件系统及数据库系统普遍采用B-Tree作为索引结构,

一般来说,索引本身也很大,不可能全部存储在内存中,因此索引往往以索引文件的形式存储的磁盘上。这样的
话,索引查找过程中就要产生磁盘I/O消耗,相对于内存存取,I/O存取的消耗要高几个数量级,所以评价一个数
据结构作为索引的优劣最重要的指标就是在查找过程中磁盘I/O操作次数的渐进复杂度。

根据B+Tree的定义,可知检索一次最多需要访问h个节点。数据库系统的设计者巧妙利用了磁盘预读原理,将一个
节点的大小设为等于一个页,这样每个节点只需要一次I/O就可以完全载入。(Innodb的数据页是16K,1.2.x支
持8K,4K压缩页)。

每次新建节点时,直接申请一个页的空间,这样就保证一个节点物理上也存储在一个页里,加之计算机存储分配都
是按页对齐的,就实现了一个node只需一次I/O。

B-Tree中一次检索最多需要h-1次I/O(根节点常驻内存),渐进复杂度为O(h)=O(logdN)。


为什么使用B+Tree
B+树是B-树的变种( PLUS版) 多路绝对平衡查找树, 他拥有B-树的优势
B+树扫库、 表能力更强(只需要扫描关键字和引用)
B+树的磁盘读写能力更强
B+树的排序能力更强
B+树的查询效率更加稳定(和数据在叶子节点有关)

什么是 B+树,B-树
https://www.cnblogs.com/vianzhang/p/7922426.html 

B+树

 

8、 Select Count (*)和Select Count(1)以及Select Count(column)区别

count(*) 包括了所有的列,相当于行数,在统计结果的时候,不会忽略列值为NULL ;

count(1) 1并不是表示第一个字段,而是表示一个固定值。其实就可以想成表中有这么一个字段,这个字段就是固定值1。就是计算一共有多少个1,在统计结果的时候,不会忽略列值为NULL 。

count(字段) 会统计该字段在表中出现的次数,忽略字段为NULL值的记录数。

一般情况下,Select Count (*)和Select Count(1)两着返回结果是一样的
假如表沒有主键(Primary key), 那么count(1)比count(*)快,
如果有主键的話,那主键作为count的条件时候count(主键)最快
如果你的表只有一个字段的话那count(*)就是最快的
count(*) 跟 count(1) 的结果一样,都包括对NULL的统计,而count(column) 是不包括NULL的统计

https://blog.csdn.net/lingduo24/article/details/87621363 

9、唯一索引,可以在索引列插入多个null吗

1,唯一索引可在索引列插入多次null
2,适用于表中的一些业务列,不能出现重复,但可以插入空值,比如用户表的身份证号码

10、写出一条sql查询出student(id,name)表中name相同id连续的三条记录

SELECT
	a.id,
	b.id,
	c.id
FROM
	student a,
	student b,
	student c
WHERE
	a.id - b.id = 1
AND b.id - c.id = 1
AND a.name= b.name
AND b.name= c.name

11、SQL语句关键字的执行顺序是什么

Java中高级面试题总览_第2张图片

这张图与 SQL 查询的语义有关,让你知道一个查询会返回什么,并回答了以下这些问题:

  • 可以在 GRROUP BY 之后使用 WHERE 吗?(不行,WHERE 是在 GROUP BY 之前!)

  • 可以对窗口函数返回的结果进行过滤吗?(不行,窗口函数是 SELECT 语句里,而 SELECT 是在 WHERE 和 GROUP BY 之后)

  • 可以基于 GROUP BY 里的东西进行 ORDER BY 吗?(可以,ORDER BY 基本上是在最后执行的,所以可以基于任何东西进行 ORDER BY)

  • LIMIT 是在什么时候执行?(在最后!)

 但数据库引擎并不一定严格按照这个顺序执行 SQL 查询,因为为了更快地执行查询,它们会做出一些优化,这些问题会在以后的文章中解释。

  • 如果你想要知道一个查询语句是否合法,或者想要知道一个查询语句会返回什么,可以参考这张图;

  • 在涉及查询性能或者与索引有关的东西时,这张图就不适用了。

https://mp.weixin.qq.com/s/ZZdWaiPk6lVDAVPLn6II9w

中间件

1.Dubbo有哪些均衡策略,和集群模式

均衡策略

1、随机(缺省),按权重设置随机概率。
在一个截面上碰撞的概率高,但调用量越大分布越均匀,而且按概率使用权重后也比较均匀,有利于动态调整提供者权重。


2、轮循,按公约后的权重设置轮循比率。
存在慢的提供者累积请求问题,比如:第二台机器很慢,但没挂,当请求调到第二台时就卡在那,久而久之,所有请求都卡在调到第二台上。



3、最少活跃调用数,相同活跃数的随机,活跃数指调用前后计数差。
使慢的提供者收到更少请求,因为越慢的提供者的调用前后计数差会越大。


4、一致性Hash,相同参数的请求总是发到同一提供者。
当某一台提供者挂时,原本发往该提供者的请求,基于虚拟节点,平摊到其它提供者,不会引起剧烈变动。

集群容错模式:

1、失败自动切换,当出现失败,重试其它服务器。(缺省)
通常用于读操作,但重试会带来更长延迟。
可通过retries="2"来设置重试次数(不含第一次,默认就是2)


2、快速失败,只发起一次调用,失败立即报错。
通常用于非幂等性的写操作,比如新增记录。


3、失败安全,出现异常时,直接忽略。
通常用于写入审计日志等操作。


4、失败自动恢复,后台记录失败请求,定时重发。
通常用于消息通知操作。


5、并行调用多个服务器,只要一个成功即返回。
通常用于实时性要求较高的读操作,但需要浪费更多服务资源。
可通过forks="2"来设置最大并行数。


6、广播调用所有提供者,逐个调用,任意一台报错则报错。(2.1.0开始支持)
通常用于通知所有提供者更新缓存或日志等本地资源信息。
重试次数配置如:(failover集群模式生效)

高并发下接口幂等性解决方案

 

2、为什么要有Dubbo的原因

各个团队的服务提供方就不要各自实现一套序列化、反序列化、网络框架、连接池、收发线程、超时处理、状态机等“业务之外”的重复技术劳动,造成整体的低效。

3.MQ分布式系统事务一致性解决方案

http://www.infoq.com/cn/articles/solution-of-distributed-system-transaction-consistency

 

 

4.redis分布式缓存

http://blog.csdn.net/javaloveiphone/article/details/52327240

http://blog.csdn.net/javaloveiphone/article/details/52352894

 

5.redis和memcached的区别

1、Redis和Memcache都是将数据存放在内存中,都是内存数据库。不过memcache还可用于缓存其他东西,例如图片、视频等等;
2、Redis不仅仅支持简单的k/v类型的数据,同时还提供list,set,hash等数据结构的存储;
3、虚拟内存--Redis当物理内存用完时,可以将一些很久没用到的value 交换到磁盘;
4、过期策略--memcache在set时就指定,例如set key1 0 0 8,即永不过期。Redis可以通过例如expire 设定,例如expire name 10;
5、分布式--设定memcache集群,利用magent做一主多从;redis可以做一主多从。都可以一主一从;
6、存储数据安全--memcache挂掉后,数据没了;redis可以定期保存到磁盘(持久化);
7、灾难恢复--memcache挂掉后,数据不可恢复; redis数据丢失后可以通过aof恢复;
8、Redis支持数据的备份,即master-slave模式的数据备份;
9、应用场景不一样:Redis出来作为NoSQL数据库使用外,还能用做消息队列、数据堆栈和数据缓存等;Memcached适合于缓存SQL语句、数据集、用户临时性数据、延迟查询数据和session等。

 

6、Zookeeper的临时节点是否有子节点

没有

7、Dubbo 为什么要选择使用zookeeper做为注册中心,而不用redis?

zookeeper的优势:
1.当提供程序意外停止时,注册表服务器可以自动删除其信息。
2.注册表服务器重新启动时,可以自动恢复所有注册数据和订阅请求。
3.会话过期后,可以自动恢复所有注册数据和订阅请求。

redis存在的问题?
redis是通过发布订阅模式完成消息的通知 但是存在几个问题:

1.服务的非自然下线需要监护中心来维护
2.redis做注册中心服务器时间必需同步,否则出现时间不对被强制过期(删除key)!
3.zookeeper支持监听,redis不支持,因此需要客户端启动多个线程进行订阅监听,对服务器有一定压力!

框架

1.SpringBoot的加载流程

https://blog.csdn.net/lzhcoder/article/details/95602585

 

你可能感兴趣的:(面试)