2.sleep和wait的区别
1. servelt是线程安全的吗
2.GC的算法。
4.怎样让一个线程放弃锁,放弃锁后什么时候能再次获取锁
3. 什么样的变量存在线程安全问题
2. ThreadLocal是什么
3. 方法锁和静态方法锁的区别
5. java io和Nio的区别,使用场景
6. 红黑树的特性
8. 如何在一个大文本中迅速找出自己的名字(正则表达式)
2.如何管理线程(介绍了各种线程池的实现)
1.jvm参数、调优
4.如何优化jvm参数(堆大小、xmx一般和xms设成一样大、永久代大小、收集器选择、收集器参数、新生代对象年龄阈值等)
2.tomcat加载基本流程,涉及到的参
9.写一个函数用来将一个字符串转换成日期(主要考察SimpleDateFormat线程安全,还有并发(用ThreadLocal来做))
10.关于synchronized的问题,
class A {
synchronized void f1(){}
synchronized void f2(){}
}
Thread T1,T2,分别调用A的f1和f2是否会相互之间有影响。
这个问题实际上很简单,就是对象锁问题。
11.String是基本类型吗,为什么可以用+操作
因为本质+的源码是这么实现的,通过stringbuilder嗯~ o(* ̄▽ ̄*)o实现+操作,再将StringBuilder转换为toString()
12 Object中的clone(深复制还是浅复制) 浅克隆
在某些场景中,我们需要获取到一个对象的拷贝用于某些处理。这时候就可以用到Java中的Object.clone方法进行对象复制,得到一个一模一样的新对象。但是在实际使用过程中会发现:当对象中含有可变的引用类型属性时,在复制得到的新对象对该引用类型属性内容进行修改,原始对象响应的属性内容也会发生变化,这就是"浅拷贝"的现象。关于浅拷贝,Object.clone()方法的描述也有说明:
/**
* Creates and returns a copy of this object. The precise meaning
* ...
* The method {@code clone} for class {@code Object} performs a
* specific cloning operation. First, if the class of this object does
* not implement the interface {@code Cloneable}, then a
* {@code CloneNotSupportedException} is thrown. Note that all arrays
* are considered to implement the interface {@code Cloneable} and that
* the return type of the {@code clone} method of an array type {@code T[]}
* is {@code T[]} where T is any reference or primitive type.
* Otherwise, this method creates a new instance of the class of this
* object and initializes all its fields with exactly the contents of
* the corresponding fields of this object, as if by assignment; the
* contents of the fields are not themselves cloned. Thus, this method
* performs a "shallow copy" of this object, not a "deep copy" operation.
*
* 上面这里已经说明了,clone()方法是浅拷贝,而不是深拷贝
*
* ...
* @return a clone of this instance.
* @exception CloneNotSupportedException if the object's class does not
* support the {@code Cloneable} interface. Subclasses
* that override the {@code clone} method can also
* throw this exception to indicate that an instance cannot
* be cloned.
* @see java.lang.Cloneable
*/
protected native Object clone() throws CloneNotSupportedException;
4.说一下NIO变成,讲NIO与传统IO作对比。NIO主要用来解决什么问题?
2.ConcurrentHashMap的并发是如何实现的?
HashMap是非线程安全的,Hashtable是线程安全的,但是由于Hashtable是采用synchronized进行同步,相当于所有线程进行读写时都去竞争一把锁,导致效率非常低下。
ConcurrentHashMap 类中包含两个静态内部类 HashEntry 和 Segment。HashEntry 用来封装映射表的键 / 值对;Segment 用来充当锁的角色,每个 Segment 对象守护整个散列映射表的若干个桶。每个桶是由若干个 HashEntry 对象链接起来的链表。一个 ConcurrentHashMap 实例中包含由若干个 Segment 对象组成的数组。
ConcurrentHashMap为了提高本身的并发能力,在内部采用了一个叫做Segment的结构,一个Segment其实就是一个类Hash Table的结构,Segment内部维护了一个链表数组,我们用下面这一幅图来看下ConcurrentHashMap的内部结构:
从上面的结构我们可以了解到,ConcurrentHashMap定位一个元素的过程需要进行两次Hash操作,第一次Hash定位到Segment, 第二次Hash定位到元素所在的链表的头部,因此,这一种结构的带来的副作用是Hash的过程要比普通的HashMap要长,但是带来的好处是写操作的时 候可以只对元素所在的Segment进行加锁即可,不会影响到其他的Segment,这样,在最理想的情况下,ConcurrentHashMap可以最 高同时支持Segment数量大小的写操作(刚好这些写操作都非常平均地分布在所有的Segment上),所以,通过这一种结 构,ConcurrentHashMap的并发能力可以大大的提高。
ConcurrentHashMap 的高并发性主要来自于三个方面:
用分离锁实现多个线程间的更深层次的共享访问。
用 HashEntery 对象的不变性来降低执行读操作的线程在遍历链表期间对加锁的需求。
通过对同一个 Volatile 变量的写 / 读访问,协调不同线程间读 / 写操作的内存可见性。
使用分离锁,减小了请求 同一个锁的频率。
通过 HashEntery 对象的不变性及对同一个 Volatile 变量的读 / 写来协调内存可见性,使得 读操作大多数时候不需要加锁就能成功获取到需要的值。由于散列映射表在实际应用中大多数操作都是成功的 读操作,所以 2 和 3 既可以减少请求同一个锁的频率,也可以有效减少持有锁的时间。
3.HashTable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数。
4.针对HashMap中某个Entry链太长,查找的时间复杂度可能达到O(n),怎么优化?
将链表转为红黑树,实现 O(logn) 时间复杂度内查找。JDK1.8 已经实现了。
正常的没有hash冲突的hash表的查询/插入/删除的时间复杂度都是o(1)
但是在发生hash冲突时,会用拉链法解决hash冲突,我们知道链表的查找时间复杂度为O(N),因此说在大量冲突的情况下,hashmap中的链就会很长,其查询效率就会变为O(N),因此hashmap的这个存储结构(数组+链表)的缺陷在jdk1.8中已经被解决,主要是在链太长的情况下,hashmap的底层结构由链表转换为红黑树,这样子查找的效率就会提高,由o(n)提升到o(logn)
8.hash冲突的四种办法
开放地址法,链地址法,建立公共溢出区,再hash
10.给你一个表只有一列name~~有重复的name, 然后求出前十个name数最大的:
select name,count(name) from table group by name desc limit 10
12.为什么java要有垃圾回收?
2、如何进行垃圾回收:
Java语言规范没有明确地说明JVM使用哪种垃圾回收算法,但是任何一种垃圾回收算法一般要做2件基本的事情:(1)发现无用信息对象;(2)回收被无用对象占用的内存空间,使该空间可被程序再次使用。
大多数垃圾回收算法使用了根集(root set)这个概念;所谓根集就是正在执行的Java程序可以访问的引用变量的集合(包括局部变量、参数、类变量),程序可以使用引用变量访问对象的属性和 调用对象的方法。垃圾回收首先需要确定从根开始哪些是可达的和哪些是不可达的,从根集可达的对象都是活动对象,它们不能作为垃圾被回收,这也包括从根集间 接可达的对象。
3、对于特殊区域的垃圾回收(比如调用cc语言中的malloc()函数进行空间的分配那么垃圾回收机制就不能回收这类特殊空间): 之所以要使用finalize(),是存在着垃圾回收器不能处理的特殊情况。假定你的对象(并非使用new方法)获得了一块“特殊”的内存区域,由于垃圾 回收器只知道那些显示地经由new分配的内存空间,所以它不知道该如何释放这块“特殊”的内存区域,那么这个时候java允许在类中定义一个由 finalize()方法
4、触发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应用将停止。
5、减少GC开销的措施:
不要显示的调用System.gc()
尽量减少临时对象的使用
对象不用的时候最好显示置空
尽量使用StringBuffer,不实用String累加字符串(String的特性有关)
能使用基本数据类型就不要使用封装类
尽量减少静态对象变量的使用
6、注意:
GC的回收时间是不确定的,即使你显示的调用的System.gc()。因为和线程优先级有关
使用了finalize()方法之后,GC是在这个方法执行之后的下一次进行垃圾的回收。
21.如何解决Java GC 导致的延迟问题.
设置gc的算法,优化程序,使用分布式jvm等等方法吧,,,,,,嗯~ o(* ̄▽ ̄*)o这是一个比较高深的问题。
大概的了解一下吧:
在我们的生产环境中,我们不断发现一些运行在JVM上的应用程序,偶尔会因为记录JVM的GC日志,而被后台的IO操作(例如OS的页缓存回写)阻塞,出现长时间的STW(Stop-The-World)停顿。在这些STW停顿的过程中,JVM会暂停所有的应用程序线程,此时应用程序会停止对用户请求的响应,这对于要求低延迟的系统来说,因此所导致的高延迟是不可接受的。
23.动态代理的原理
AOP的原理就是java的动态代理机制。在java的动态代理机制中,有两个重要的类或接口,一个是 InvocationHandler(Interface)、另一个则是 Proxy(Class)。每一个动态代理类都必须要实现InvocationHandler这个接口,并且每个代理类的实例都关联到了一个handler,当我们通过代理对象调用 一个方法的时候,这个方法的调用就会被转发为由1)1) InvocationHandler这个接口的 invoke 方法来进行调用。
Object invoke(Object proxy, Method method, Object[] args) throws Throwable
proxy: 指代我们所代理的那个真实对象
method: 指代的是我们所要调用真实对象的某个方法的Method对象
args: 指代的是调用真实对象某个方法时接受的参数
Proxy这个类的作用就是用来动态创建一个代理对象的类,它提供了许多的方法,但是我们用的最多的就是 newProxyInstance 这个方法:
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException
loader: 一个ClassLoader对象,定义了由哪个ClassLoader对象来对生成的代理对象进行加载
interfaces: 一个Interface对象的数组,表示的是我将要给我需要代理的对象提供一组什么接口,如果我提供了一组接口给它,那么这个代理对象就宣称实现了该接口(多态),这样我就能调用这组接口中的方法了
h: 一个InvocationHandler对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个InvocationHandler对象上
25.包装类型和基本类型比较问题(例如,Integer类型的变量能否==int类型变量,能否作比较,什么时候不能作比较)
一个包装对象和一个基本类型的值进行比较是比较值,而不是比较引用。
26.java锁机制
把代码块声明为 synchronized,有两个重要后果,通常是指该代码具有 原子性(atomicity)和 可见性(visibility)。
volatile只保证可见性,不保证原子性!
ReentrantLock 类实现了Lock ,它拥有与synchronized 相同的并发性和内存语义,但是添加了类似锁投票、定时锁等候和可中断锁等候的一些特性。此外,它还提供了在激烈争用情况下更佳的性能。
用sychronized修饰的方法或者语句块在代码执行完之后锁自动释放,而是用Lock需要我们手动释放锁,所以为了保证锁最终被释放(发生异常情况),要把互斥区放在try内,释放锁放在finally内!!
读写锁ReadWriteLock,与互斥锁定相比,读-写锁定允许对共享数据进行更高级别的并发访问。虽然一次只有一个线程(writer 线程)可以修改共享数据,但在许多情况下,任何数量的线程可以同时读取共享数据(reader 线程)。
31.重入锁、对象锁、类锁的关系
对象锁(方法锁),是针对一个对象的,它只在该对象的某个内存位置声明一个标识该对象是否拥有锁,所有它只会锁住当前的对象,一般一个对象锁是对一 个非静态成员变量进行synchronized修饰,或者对一个非静态成员方法进行synchronized进行修饰,对于对象锁,不同对象访问同一个被 synchronized修饰的方法的时候不会阻塞
类锁是锁住整个类,当有多个线程来声明这个类的对象时候将会被阻塞,直到拥有这个类锁的对象呗销毁或者主动释放了类锁,这个时候在被阻塞的线程被挑选出一个占有该类锁,声明该类的对象。其他线程继续被阻塞住。
.32.一个项目,理论上需要1.5G的内存就足够,但是项目上线后发现隔了几个星期,占用内存到了2.5G,这时候你会考虑是什么问题?怎么解决?
我回答会首先考虑内存泄漏,使用MemeryAnalyzer之类的工具分析检查哪里有泄漏
38.哪些方法实现线程安全。
synchronized,reentrantlock,volatile,然后重点说了下volatile在某些情况下可以实现线程安全,然后就把面试官注意力往volatile上引,因为volatile这个专门看了一下,果然,面试官马上问了volatile。
---------------------
40.如果想实现一个线程安全的队列,可以怎么实现?
JUC包里的ArrayBlockingQueue 还有LinkedBlockingQueue啥的又结合源码说了一通
13.final关键字
类:在使用final修饰类的时候,要注意谨慎选择,除非这个类真的在以后不会用来继承或者出于安全的考虑,尽量不要将类设计为final类。final类中的方法默认是final的,变量则不是;
方法:只有在想明确禁止 该方法在子类中被覆盖的情况下才将方法设置为final的。注:类的private方法会隐式地被指定为final方法。
变量:对于一个final变量,如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改;如果是引用类型的变量,则在对其初始化之后便不能再让其指向另一个对象。
引用变量被final修饰之后,虽然不能再指向其他对象,但是它指向的对象的内容是可变的。
16.transient关键字:
这个字段的生命周期仅存于调用者的内存中而不会写到磁盘里持久化。
一个对象只要实现了Serilizable接口,这个对象就可以被序列化,java的这种序列化模式为开发者提供了很多便利,我们可以不必关系具体序列化的过程,只要这个类实现了Serilizable接口,这个类的所有属性和方法都会自动序列化。
总之,java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中。
1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。
2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。
3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。
若实现的是Externalizable接口,则没有任何东西可以自动序列化,需要在writeExternal方法中进行手工指定所要序列化的变量,这与是否被transient修饰无关。
——————————————————
--------------------