Web后台开发之CVTE的面试经历

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/weixin_39453325/article/details/82843232

前几天(180922)收到CVTE面试短信,要我今天(180925)去长沙美爵酒店7楼面试。今天下午四点的面试我三点多就到了,前台接待员挺好的,一见到有人去,赶紧上来做引导。大概四点二十的时候到我了。下面我说一下面试官问的问题吧(自我介绍就不谈了)。

一 为什么要用Netty框架?

详情请参考https://blog.csdn.net/weixin_39453325/article/details/82995356这篇博客

二 Netty框架的实现,大概问的是Netty的一个框架流程?

详情请参考https://blog.csdn.net/weixin_39453325/article/details/82995356这篇博客

三 Tcp的拆包与粘包问题?

详情请参考https://blog.csdn.net/weixin_39453325/article/details/84181805这篇博客

四 Struts2与SpringMVC的不同点?

1. Struts2 和 SpringMVC 的框架加载机制不同。

Struts2 入口是 Filter ,而 SpringMVC 入口是 Servlet (DispatcherServlet)。Filter在容器启动之后即初始化,服务停止后销毁,但晚于 Servlet 。Servlet 是在调用时初始化的,先于 Filter 调用,服务停止后销毁。

2. Struts2 和 SpringMVC 的框架拦截机制不同。

2.1 Struts2

  •  Struts2 框架是类级别的拦截,每次有请求就会创建一个 Action ,和 Spring 整合时 Struts2 的 ActionBean 注入作用域是原型模式 prototype(不然会出现线程并发问题),然后调用 setter ,getter 把 request 参数注入到对象的属性中去(属性驱动和模型驱动)。
  •  Struts2 中,一个 Action 对应一个 request ,response 上下文,在接收参数时,可以通过属性接收,说明属性参数是可以让多个方法共享的,采用值栈存储请求以及相应数据,OGNL 存取数据。
  •  Struts2 中 Action 的一个方法可以对应一个 url ,而其类属性却被所有方法共享,这也就无法用注解或其他方法标识其属性方法了。
  •  Intercepter 的实现机制,Struts2 有自己的拦截机制 Interceptor。

2.2 SpringMVC

  •  SpringMVC 是方法级别的拦截,一个方法对应一个 Request 上下文,所以方法基本上是独立的,独享 request ,response 数据。而每个方法同时又和一个 url 对应,参数的传递是根据参数上的注解直接注入到方法中的,是方法独享的,处理结果通过 ModelAndView 对象存储(Model 存储数据,View 存储返回的页面),最后通过 request 返回到页面。
  •  在 Sping 整合时,SpringMVC 的 Controller Bean 默认时单例模式(Singleton),所以在默认情况下对所有的请求,只会创建一个 Controller ,因为没有共享的属性,所以是线程安全的,如果要改变默认的作用域,需要添加 @Scope 注解修改。
  • Intercepter 的实现机制,SpringMVC 使用独立的 AOP 方式,导致配置文件比 Struts2 的配置文件多。

3. 框架集成方面。

SpringMVC 和 Spring 是无缝的(无需数据格式的转换,直接访问来自数据源数据格式)。项目的管理和安全上比 Struts2 高。

4. Ajax 交互。

SpringMVC 处理 Ajax 的请求十分方便,只需一个注解 @ResponseBody,然后直接返回响应文本;

Struts2 拦截器集成了 Ajax ,在 Action 中处理时一般需要安装插件或者自己写代码集成进去,使用起来相对不方便。

五 为什么要用Spring?

我回答了用来整合Struts2和Hibernate,还讲了IOC与AOP。

Spring 是一个开源框架,为了解决企业应用开发的复杂性而创建的,是一个轻量级的 IOC (控制反转) 和 AOP (面向切面)的容器框架。所有对象的创建和依赖关系的维护以及生命周期可以由 Spring 容器统一管理(有效降低耦合度);只需要通过配置即可完成对事务的管理,而无需手动编程(声明式事务的支持);提供对 Junit 的支持,可以通过注解方便的测试程序(易于单元测试)。

  • IOC (控制反转):控制权的转移,应用程序本身不在负责依赖对象的创建与维护,而是由外部容器负责创建和维护。谁控制谁:IOC 容器控制了对象;控制了什么:控制了外部资源获取(不只是对象的创建,还有文件之类的);为何是反转:因为容器帮我们查找注入依赖的对象,对象只是被动的接受依赖的对象,所以是反转;哪些方面反转:依赖对象的获取被反转了。
  • DI (依赖注入):是 IOC 的一种实现方式,其目的是创建对象并且组装对象之间的关系。
  • AOP (面向切面):在程序开发中主要用来解决一些系统层面上的问题,比如事件管理,安全检查,缓存,对象池管理,日志管理等,利用一种称为“横切”的技术,把软件系统分为两个部分“核心关注点”和“横切关注点”,AOP的作用载与分离系统中的各种关注点,将核心关注点与横切关注点分离开。其底层实现是使用了动态代理模式,大家可以参考这篇博客https://blog.csdn.net/weixin_39453325/article/details/84309742

六 数据库有哪些引擎?

详情请参考https://blog.csdn.net/weixin_39453325/article/details/83142151这篇博客

七 数据库的优化有哪一些,索引是怎么使用的?

详情请参考https://blog.csdn.net/weixin_39453325/article/details/83472961这篇博客

八 都用过哪些集合框架?

  • Collection 接口:为 List 和 Set 声明了集合的基本操作方法,包括获得迭代器、集合是否包含指定元素、增加或删除集合元素、集合运算、获得子集等。

  • Iterator 接口:Java 采用迭代方式实现对集合中的元素进行遍历和删除操作。迭代指从集合中获得元素的后继元素。迭代功能由 Iterable 可迭代接口和 Iterator、ListIterator 迭代器接口实现。

  • Map 接口:Map 接口声明从关键字到值的映射。实现 Map 接口的类主要有:使用散列表的 HashMap 散列映射类和使用平衡二叉树的 TreeMap 树映射类,TreeMap 类实现按关键字排序的 SortedMap 有序映射接口。

九 什么情况下用ArrayList,什么情况下用LinkedList?

ArrayList 数组列表类使用一维数组实现 List,具有随机存取特征(存取元素的时间复杂度是O(1)),插入、删除元素时需要移动其他元素,当元素很多时,插入和删除操作的平均时间效率较低。

LinkedList 链表类使用循环双链表实现 List ,插入、删除元素时不需要移动其他元素,但不具有随机存取的特性。

  • 当操作是在数据后面添加数据而不是在前面或中间,并且需要随机访问其中的元素时,使用 ArrayList 比较好。
  • 当操作是在数据的前面或中间添加删除数据,并且按照顺序访问元素时,应该使用 LinkedList 。

十 HashMap的底层实现。我回答了get与put方法以及扩容,为什么HashMap的大小是2^n。

详情请参考 https://blog.csdn.net/weixin_39453325/article/details/82953747 这篇博客。

十一 如何实现线程安全的HashMap?提示了我ConcurrentHashMap 。

HashMap 是线程不安全的

1. 假设正好存在两个 put 操作的 key 发生碰撞(hash 值相同),那么根据 HahMap 的实现,这两个 key 会添加到数组的同一个位置,这样会发生其中一个 key 的 value 值被覆盖的情况。

2. 如果多个线程同时检测到到元素的个数超过(数组大小 * 负载因子),这样会发生多个线程同时对 Node 数组进行扩容,都在重新计算元素位置以及复制数据,但是最终只有一个线程扩容后的数组会赋给 table,也就是说其他线程都会丢失,并且各自线程的 put 数据也会丢失。

那么如何实现线程安全的 HashMap 呢?

1 HashTable:使用 synchronized 来保证线程安全,当一个线程访问 HashTable 的同步方法时,其他的线程也要访问的话会被阻塞。

public synchronized V get(Object key) {
       // 省略实现
    }
public synchronized V put(K key, V value) {
    // 省略实现
    }

2 Collections.synchronizedMap:从下面的源码中可以看出调用 synchronizedMap() 方法后会返回一个 SynchronizedMap 类的对象,而在 SynchronizedMap 类中使用了 synchronized 同步关键字来保证对 Map 的操作都是线程安全的(一个时间内只能有一个线程得到执行,其他线程必须等待当前的线程执行完之后才能执行)。

  SynchronizedMap(Map<K,V> m) {
            this.m = Objects.requireNonNull(m);
            mutex = this;
        }

  SynchronizedMap(Map<K,V> m, Object mutex) {
            this.m = m;
            this.mutex = mutex;
        } 
public V get(Object key) {
            synchronized (mutex) {return m.get(key);}
        }

 public V put(K key, V value) {
            synchronized (mutex) {return m.put(key, value);}
        }

3 ConcurrentHashMap: ConcurrentHashMap 与 HashMap 相比,最关键的一个概念是:segment,segment 其实就是一个 HashMap 。segment 也包含一个 HashEntry 数组,数组中的每一个 HashEntry 既是一个键值对,也是一个链表的头节点。

每个 segment 的读写都是高度自治的,segment 之间互不影响,这称之为“锁分段技术”,在并发情况下的 ConcurrentHashMap 

情景一:不同的 segment 并发写入

情景二:同一 segment 并发写入

 情景三:同一 segment 一写一读

get 操作:

  • 为输入的 key 做 hash 运算,得到 hash 值
  • 通过 hash 值,定位到对应的 segment 对象
  • 再次通过 hash 值,定位到 segment 中数组的具体位置

put 操作: 

  • 为输入的 key 做 hash 运算,得到 hash 值
  • 通过 hash 值,定位到对应的 segment 对象
  • 获得可重入锁
  • 再次通过 hash 值,定位到 segment 当中数组的具体位置
  • 插入或覆盖 hashEntry 对象
  • 释放锁

十二 Java中都有哪些方式来保证线程安全?

1 同步互斥(悲观并发)

synchronized关键字: 是一个重量级的锁,因为线程的切换需要操作系统的调用。在编译前后会在同步块的前后分别形成 monitorentry 和 monitorexit 这两个字节码指令,它们两都需要一个 reference 类型的参数来指明要锁定和解锁的对象。在执行 monitorenter 指令时,首先会尝试获取对象的锁,如果该对象没有被锁定,或者当前线程已经拥有了那个对象的锁,则把锁的计数器加一。如获取对象失败,当前线程就要阻塞等待,直到对象锁被另一个线程释放。

ReentrantLock:支持重入锁,还支持等待中断,支持公平锁(指按照申请的时间顺序进行分配,synchronized 锁是非公平的),还支持绑定多个条件,可以同时绑定多个 condition 对象。

2 非阻塞同步(基于冲突检测的乐观并发)

互斥同步又叫阻塞同步,是一种悲观的并发策略,总是认为只要不做正确的同步措施,就肯定会出现问题,即无论是否出现共享数据的竞争,都会给资源加锁,用户态核心态转换,维护锁计数器,检查被阻塞的线程是否需要唤醒操作。

非阻塞同步是一种乐观的并发处理策略,是一种基于冲突检测的乐观并发处理,如果共享数据没有出现其他竞争,则操作成功,但当竞争出现时,会不断重试,直到成功。CAS 是实现非阻塞同步的计算机指令,它有三个操作数,内存位置,旧的预期值,新值,在执行 CAS 操作时,当且仅当内存地址的值符合旧的预期值的时候,才会用新值来更新内存地址的值,否则不执行更新。

3 无同步方案

线程本地存储:将共享数据的可见范围限制在一个线程中,这样就无需同步也能保证线程之间不会出现数据争用问题,经常使用的是 ThreadLocal 类。当多个线程访问同一个变量的时候,ThreadLocal 会为每一个线程创建一个该变量的副本,每个线程可以访问自己的副本变量。

十三 java中的CAS?

详情请参考 https://mp.csdn.net/postedit/82964401 这篇博客。

十四 编程题:给定一个数组,返回数组中关键字重复的次数,复杂度O(n)。

我当时自己写的,没想到方法,写了一个O(n^2)的,后面面试官提示HashMap我恍然大悟,这不就是我在LeetCode上的第一道算法题中的一个子问题吗。有点智障了...

	public static int total(int a[]) {
		int max=0;
		for(int i=0;i<a.length;i++) {
			int key=a[i];
			int j=0;
			int num=0;
			while(j<a.length) {
				if(key==a[j])
					num++;
				j++;
			}
			if(num>max) {
				max=num;
			}
		}
		return max;
	}

正确的是:

     private static int total(int[] a) {
		HashMap<Integer,Integer> hm=new HashMap<Integer,Integer>();
		int max=1;
		for(int i=0;i<a.length;i++) {
			if(!hm.containsKey(a[i])){
				hm.put(a[i], 1);
			}
			else {
				int num=hm.get(a[i]);
				hm.put(a[i],num+1);
				if(max<num+1) {
					max=num+1;
				}
			}
		}
		return max;
	}

猜你喜欢

转载自blog.csdn.net/weixin_39453325/article/details/82843232
今日推荐