面试实题:No.19

1、介绍一下你的项目;

最近的项目是重点。

面试官您好,我最近做的是互联网类型的项目。其中主要负责了购物车、日志、商品等模块,主要使用到了Redis、RocketMQ、dubbo等技术。我具体和您讲一下购物车模块吧!

购物车模块中我主要使用到了Redis来做缓存,降低访问并发从而提高效率,在做技术选型的时候对比了MongoDB和memcached等(具体特性交给大家了)最好考虑到Redis的多种数据类型方便后期项目拓展以及可持久化缓存等选用了Redis;

在使用过程中使用了hash类型(结合项目需要以及技术特性具体准备);

在项目开发过程中搭建了Redis集群(集群是个大点,具体搭建的过程可以具体进行准备);

项目开发过程中还遇到了消息丢失的问题;

问题的产生原因,解决方案,收获可以具体准备和面试官唠唠!

这里只是给大家举个例子,不妨参考一下!

2、jdk1.8 1.9 10 的新特性都有哪些;

jdk1.8:

1、接口的默认方法;

2、Lambda 表达式;

3、函数式接口;

4、方法与构造函数引用;

5、Lambda 作用域;

6、访问局部变量;

7、访问对象字段与静态变量;

8、访问接口的默认方法;

9、Date API;

10、Annotation 注解等。

jdk1.9:

1、Java 平台级模块系统;

2、Linking;

3、JShell : 交互式 Java REPL;

4、改进的 Javadoc;

5、集合工厂方法;

6、改进的 Stream API;

7、私有接口方法;

8、HTTP/2;

9、多版本兼容 JAR等。

其中具体的区别应用,可以准备几例哦!

3、线程安全的集合都有哪些;

一、概念:

* 线程安全:就是当多线程访问时,采用了加锁的机制;即当一个线程访问该类的某个数据时,会对这个数据进行保护,其他线程不能对其访问,直到该线程读取完之后,其他线程才可以使用。防止出现数据不一致或者数据被污染的情况。

* 线程不安全:就是不提供数据访问时的数据保护,多个线程能够同时操作某个数据,从而出现数据不一致或者数据污染的情况。

* 对于线程不安全的问题,一般会使用synchronized关键字加锁同步控制。

* 线程安全工作原理: jvm中有一个main memory对象,每一个线程也有自己的working memory,一个线程对于一个变量variable进行操作的时候, 都需要在自己的working memory里创建一个copy,操作完之后再写入main memory。 当多个线程操作同一个变量variable,就可能出现不可预知的结果。

而用synchronized的关键是建立一个监控monitor,这个monitor可以是要修改的变量,也可以是其他自己认为合适的对象(方法),然后通过给这个monitor加锁来实现线程安全,每个线程在获得这个锁之后,要执行完加载load到working memory 到 use && 指派assign 到 存储store 再到 main memory的过程。才会释放它得到的锁。这样就实现了所谓的线程安全。

二、线程安全(Thread-safe)的集合对象:

* Vector 线程安全:

* HashTable 线程安全:

* StringBuffer 线程安全:

三、非线程安全的集合对象:

* ArrayList :

* LinkedList:

* HashMap:

* HashSet:

* TreeMap:

* TreeSet:

* StringBulider:

相互之间的对比区别等建议也进行准备。

4、object中的hashcode()方法是做什么用的;

我们在equals方法中需要向下转型,效率很低,所以先判断hashCode方法可以提高效率。

equals()相等的两个对象,hashcode()一定相等; equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。换句话说,equals()方法不相等的两个对象,hashcode()有可能相等。

反过来:hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

5、用户A 访问服务器A 在A中存储了你的session  但是A再次访问的时候访问了B服务器  如何保证你的session共享给B;

1、监听器和触发器,当访问服务器A时,触发条件将登陆信息同步至服务器B;

2、Redis缓存和消息中间件的应用。

此问题类似于单点登录问题的处理方面,可以往这个方向着手。

6、gc的垃圾回收算法;

1、标记清除算法:从GC Root遍历标记,未被标记的清除;

2、标记整理算法:从GC Root标记遍历,将标记到的对象整理至内存一侧,然后清除某一地址后的对象;

3、复制算法:将内存一分为二,只是用其中一个内存作为活动区,未被使用的为空闲区,从GC Root标记遍历,被标记的算法从活动区规则的排列在空闲区,然后将活动区清空,交换空闲区和活动区,在对象存活率低的时候使用;

4、分代搜集算法。

7、在jvm中如何查看你的每个线程的堆栈信息;

jstack -l <pid> >jvm_listlocks.txt

8、为什么进行垃圾回收;

1、为什么要进行垃圾回收:

       在C++中,对象所占的内存在程序结束运行之前一直被占用,在明确释放之前不能分配给其它对象;而在Java中,当没有对象引用指向原先分配给某个对象 的内存时,该内存便成为垃圾。 垃圾回收能自动释放内存空间,减轻编程的负担,JVM的一个系统级线程会自动释放该内存块。垃圾回收意味着程序不再需要的对象是"无用信息",这些信息将被丢弃。当一个对 象不再被引用的时候,内存回收它占领的空间,以便空间被后来的新对象使用。事实上,除了释放没用的对象,垃圾回收也可以清除内存记录碎片。由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。

2、垃圾回收机制的意义

Java语言中一个显著的特点就是引入了垃圾回收机制,使c++程序员最头疼的内存管理的问题迎刃而解,它使得Java程序员在编写程序的时候不再需要考虑内存管理。由于有个垃圾回收机制,Java中的对象不再有“作用域”的概念,只有对象的引用才有“作用域”。垃圾回收可以有效的防止内存泄露,有效的使用空闲的内存。

ps:内存泄露是指该内存空间使用完毕之后未回收,在不涉及复杂数据结构的一般情况下,Java 的内存泄露表现为一个内存对象的生命周期超出了程序需要它的时间长度,我们有时也将其称为“对象游离”。

3、触发GC(Garbage Collector)的条件:

 JVM进行次GC的频率很高,但因为这种GC占用时间极短,所以对系统产生的影响不大。更值得关注的是主GC的触发条件,因为它对系统影响很明显。总的来说,有两个条件会触发主GC:

  1)当应用程序空闲时,即没有应用线程在运行时,GC会被调用。因为GC在优先级最低的线程中进行,所以当应用忙时,GC线程就不会被调用,但以下条件除外。

  2)Java堆内存不足时,GC会被调用。 当应用线程在运行,并在运行过程中创建新对象,若这时内存空间不足,JVM就会强制地调用GC线程,以便回收内存用于新的分配。若GC一次之后仍不能满足 内存分配的要求,JVM会再进行两次GC作进一步的尝试,若仍无法满足要求,则 JVM将报“out of memory”的错误,Java应用将停止。

4、减少GC开销的措施:

不要显示的调用System.gc()

尽量减少临时对象的使用

对象不用的时候最好显示置空

尽量使用StringBuffer,不实用String累加字符串(String的特性有关)

能使用基本数据类型就不要使用封装类

尽量减少静态对象变量的使用

5、注意:

GC的回收时间是不确定的,即使你显示的调用的System.gc()。因为和线程优先级有关

使用了finalize()方法之后,GC是在这个方法执行之后的下一次进行垃圾的回收。

9、threadLocal 谈谈你的了解;

一、ThreadLocal是什么

从名字我们就可以看到ThreadLocal叫做线程变量,意思是ThreadLocal中填充的变量属于当前线程,该变量对其他线程而言是隔离的。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。

从字面意思来看非常容易理解,但是从实际使用的角度来看,就没那么容易了,作为一个面试常问的点,使用场景那也是相当的丰富:

1、在进行对象跨层传递的时候,使用ThreadLocal可以避免多次传递,打破层次间的约束。

2、线程间数据隔离

3、进行事务操作,用于存储线程事务信息。

4、数据库连接,Session会话管理。

现在相信你已经对ThreadLocal有一个大致的认识了,下面我们看看如何用?

二、ThreadLocal怎么用

每一个线程都有各自的local值,我们设置了一个休眠时间,就是为了另外一个线程也能够及时的读取当前的local值。

我们使用数据库的时候首先就是建立数据库连接,然后用完了之后关闭就好了,这样做有一个很严重的问题,如果有1个客户端频繁的使用数据库,那么就需要建立多次链接和关闭,我们的服务器可能会吃不消,怎么办呢?如果有一万个客户端,那么服务器压力更大。

这时候最好ThreadLocal,因为ThreadLocal在每个线程中对连接会创建一个副本,且在线程内部任何地方都可以使用,线程之间互不影响,这样一来就不存在线程安全问题,也不会严重影响程序执行性能。是不是很好用。

三、ThreadLocal源码分析

1、set方法

从set方法我们可以看到,首先获取到了当前线程t,然后调用getMap获取ThreadLocalMap,如果map存在,则将当前线程对象t作为key,要存储的对象作为value存到map里面去。如果该Map不存在,则初始化一个。

2、get方法

首先获取当前线程,然后调用getMap方法获取一个ThreadLocalMap,如果map不为null,那就使用当前线程作为ThreadLocalMap的Entry的键,然后值就作为相应的的值,如果没有那就设置一个初始值。

3、remove方法

(1)每个Thread维护着一个ThreadLocalMap的引用

(2)ThreadLocalMap是ThreadLocal的内部类,用Entry来进行存储

(3)ThreadLocal创建的副本是存储在自己的threadLocals中的,也就是自己的ThreadLocalMap。

(4)ThreadLocalMap的键值为ThreadLocal对象,而且可以有多个threadLocal变量,因此保存在map中

(5)在进行get之前,必须先set,否则会报空指针异常,当然也可以初始化一个,但是必须重写initialValue()方法。

(6)ThreadLocal本身并不存储值,它只是作为一个key来让线程从ThreadLocalMap获取value。

四、ThreadLocal其他几个注意的点

1、Thread中有一个map,就是ThreadLocalMap

2、ThreadLocalMap的key是ThreadLocal,值是我们自己设定的。

3、ThreadLocal是一个弱引用,当为null时,会被当成垃圾回收

4、重点来了,突然我们ThreadLocal是null了,也就是要被垃圾回收器回收了,但是此时我们的ThreadLocalMap生命周期和Thread的一样,它不会回收,这时候就出现了一个现象。那就是ThreadLocalMap的key没了,但是value还在,这就造成了内存泄漏。

解决办法:使用完ThreadLocal后,执行remove操作,避免出现内存溢出情况。

10、StringBuilder,StringBuffer二者的区别;

HashTable是线程安全的,很多方法都是synchronized方法,而HashMap不是线程安全的,但其在单线程程序中的性能比HashTable要高。StringBuffer和StringBuilder类的区别也在于此,新引入的StringBuilder类不是线程安全的,但其在单线程中的性能比StringBuffer高。

采用String对象时,即使运行次数仅是采用其他对象的1/100,其执行时间仍然比其他对象高出25倍以上;而采用StringBuffer对象和采用StringBuilder对象的差别也比较明显,前者是后者的1.5倍左右。由此可见,如果我们的程序是在单线程下运行,或者是不必考虑到线程同步问题,我们应该优先使用StringBuilder类;当然,如果要保证线程安全,自然非StringBuffer莫属了。

除了对多线程的支持不一样外,这两个类的使用几乎没有任何差别,上面的例子就是个很好的说明。

11、stringBuffer 底层代码是什么;

(1)线程安全的字符串操作类

(2)通过synchronized关键字声明同步方法,保证多线程环境下数据安全

(3)底层存储数据的Char[]数组,初始化时,该数组的长度是16。如果构造函数有新传入字符转str,则16基础上加str.length.

(4)添加字符串的过程

-->先检查内部char[]数组是否需要扩容

-->如需要扩容则进行扩容,然后将原来元数据copy到新数组中。

-->再将新添加的元数据加入到新char[]数组中

12、== 和equals 有什么区别;

很多网友都说equals比较的是对象的内容,这样的说法是不准确的。

在Object类型的equals方法是直接通过==来比较的,和==是没有任何区别的。

那么为什么又要说equlas和==的区别呢?是因为equals方法是可以由我们自己重写的。

众所周知,我们所有的类都直接或间接地继承自java.lang.Object类,因此我们可以通过重写equals方法来实现我们自己想要的比较方法。

这个方法体是完全可以由我们自己实现的,即便是我们直接 return true 都是可以的,只要能满足我们的业务需求,怎样写都是无所谓的,因此,equals比较的并不一定是对象的内容,它还可以由其他的信息来指导比较。

13、equals 的底层代码是什么。

equals底层也是==来执行的,具体代码这里就不赘述了。

发布了441 篇原创文章 · 获赞 1021 · 访问量 53万+

猜你喜欢

转载自blog.csdn.net/A_BlackMoon/article/details/104947088
今日推荐