经典类概念性问题
16.”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?
普通概念性问题
26.Static Nested Class 和 Inner Class的不同(内部类和静态内部类)
30.Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?
70.java中有几种类型的流?JDK为每种类型提供了一些抽象类以供继承,请分别说出他们是哪些类?
反射
异常
27.JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
关键字
3.String 和StringBuffer以及StringBuilder的区别
42.Iterator和ListIterator的区别是什么?
45.Collection 和 Collections的区别。
25.final, finally, finalize的区别
接口:
24.Comparable和Comparator接口是干什么的?列出它们的区别。
41.为什么集合类没有实现Cloneable和Serializable接口?
多线程
7.介绍一下Synchronized锁,如果用这个关键字修饰一个静态方法,锁住什么?如果修饰成员方法,锁住什么?
43.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
56.在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
52.cyclicbarrier和countdownlatch的区别
59.stop()和suspend()方法为何不推荐使用,请说明原因?
60.线程的sleep()方法和yield()方法有什么区别?
线程池
JAVA虚拟机
*HotSpot虚拟机对象探秘(对象创建,对象内存布局,对象访问定位)
*JAVA 垃圾收集算法,垃圾收集器与内存分配策略(内容全面,解析简单易懂)
JAVA设计模式
Map:
36.concurrenthashmap有啥优势,1.7,1.8区别?
List:
37.ArrayList和LinkedList的区别,如果一直在list的尾部添加元素,用哪个效率高?
44.ArrayList,Vector,LinkedList的存储性能和特性是什么?
基本数据类型:
整型:byte short int long
字符型:char
浮点型:float double
布尔型:boolean
整数默认int,小数默认double,float和long必须加后缀(因为默认double,转float会损失精度,所以不会自动转,如果超过了int范围没有加L后缀,那么也会报错)
String类型属于引用类型,引用类型声明的变量是指该变量在内存中实际存储的是一个引用地址,实体在堆中,引用类型包括类,数组,接口等,而包装类也属于引用类型,自动装箱,拆箱就是基本类型和引用类型(此处为包装类型)间的转换,而转换为引用类型的话,就new一个对象,从而可以调用包装类型中封装好的方法,方便使用!
1.int是基本数据类型,Integer的引用数据类型
2.int默认0,Integer默认null
3.int存储在栈中,Integer存储在堆中
4.int变量存储数据原始值,Integer变量存储的是引用地址
5.Integer通过实例化创建的,是对象!
3.String 和StringBuffer以及StringBuilder的区别
String 字符串常量
StringBuffer 字符串变量(线程安全)
StringBuilder 字符串变量(线程不安全)
字符串常量创建后长度,内容都不可以改变,可以new,也可以直接“abc”这样给值
字符串变量倍创建后长度,内容都是可以被修改的,只能通过new创建!
&&和&都是判断两边表达式是否为真,使用&时,如果左边为假,那么它还会去验证右边,但是使用&&时,如果左边为假,它就不会验证右边了,具有短路的效果,效率比单&高!
java:面向对象 需要编译再进行运行 属于强类型(编译时才能确定变量类型)
javascript:基于对象和事件驱动 解释型语言 弱类型(执行时才能确定变量类型)
通过String类中的一些方法,比如matchs(),replaceAll(),replaceFirst(),split()等
7.介绍一下Synchronized锁,如果用这个关键字修饰一个静态方法,锁住什么?如果修饰成员方法,锁住什么?
Syscronized锁是同步锁,如果关键字修饰静态方法的话是一个类锁(当前类的所有线程都必须等待同步线程执行),如果关键字修饰成员方法的话是一个对象锁(当前对象的所有进程必须等待同步进程执行完,释放锁)
(静态方法:可以不用生成实例对象而直接引用)
Volatile关键字修饰共享变量,保证其他线程访问这个变量的时候始终是最新值,也就是Volatile会更新最新值到java主内存中,其他线程使用这个变量的时候会从java主内存中去取得这个变量(非volatiel不具备这个特性,非volatile变量在被某个线程修改之后会被缓存,线程A更新了这个值,线程B读取到的可能并不是最新值),volatile不具备原子性(读volatile具备原子性,但volatile变量i的i++操作不具备原子性),这是volatile与synchrozied,lock的最大差异!
java为某个共享资源的同步提供了两种锁机制:Synchrozied和lock
二者有以下区别:
1.用法不一样:在需要同步的对象中加入Synchrozied锁,Synchrozied既可以加载在方法的前面也可以加载在特定的代码块中,括号表示需要锁的对象,而lock需要显示的指定起始位置和终止位置,Synchrozied是托管给JVM执行的,而lock的锁定是通过开发人员手动代码实现的
2.性能不一样:jdk5中增加了一个lock接口的实现类ReentrantLock,他们的性能在资源不同的情况下会有很大的不同:在资源竞争不是很激烈的情况下,synchorized的性能要优于ReentrantLock,但是在资源竞争很激烈的情况下,synchoorized的性能会下降很快而ReentrantLock的性能会基本保持不变
3.锁机制不一样:synchorized获得锁和释放锁的方式都在结构中,当获取多个锁时间,必须以相反的方式释放锁,并且自动解锁,不会应用出现异常而引发死锁,而Lock则需要开发人员手动释放,并且必须在final块中释放,否则会引发死锁
4.灵活性不一样:比如ABC三个线程,两个读文件一个写文件,synchorized只能依次枷锁和解锁,而lock可以让读共享,这样更好,所以后面就引发了锁优化技术
被final修饰的类不能继承,被final修饰的fan方法不能重写,被final修饰的变量为常量,值不能改变
object中的方法,可以暂停线程,期间会释放对象锁,不像sleep方法,线程休眠期间依然持有锁,wait方法的线程必须调用notify或notifyall方法唤醒线程
比如:当一个线程执行到wait方法时,它就进入到一个和对象相关的等待池中,同时失去对象锁,当它被一个notify方法唤醒时,等待池中的线程就被放到了锁池中,该线程从锁池获得对象锁,然后回到wait前的中断现场
封装,继承,多态,多态可以理解为一致类型,不同形态,比如Animal animal=new Dog()
因为String类被final修饰,string类的底层数组也是被final修饰的
1.提高字符串常量池的效率和安全,如果你知道一个对象是不可变的,那么拷贝对象的内容时就不用复制它本身而只用复制它的地址,复制地址需要很小的内存,效率也很好
2.对多线程安全,多线程的情况下,一个可变对象的值可能会被多个线程修改造成不可预期的结果,而不可变对象就不存在这个问题
1.clone:创建并返回此对象的一个副本
2.equals:比较“相等”
3.finalize:当垃圾回收器确定不存在该对象的引用时,由对象的垃圾回收器调用方法
4.getclass:返回它的运行时类
5.hashcode:返回对象的哈希码
6.notify:唤醒此对象监视器上等待的单个线程(随机唤醒)
7.notifyall:换线此对象监视器上等待的所有线程
8.tostring:返回对象的字符串表示
9.wait:暂停当前线程(可以设置超时时间)
重写是针对父类和子类的概念,重载是针对一个类中的概念,相同参数不同返回值不可以重载,因为重载必须改变参数列表,否则虚拟机怎么知道要调用哪一个
16.”static”关键字是什么意思?Java中是否可以覆盖(override)一个private或者是static的方法?
static关键字表明一个成员变量或者成员方法可以在没有所属类的实例变量的情况下被访问
java中static方法不能被覆盖,因为方法的覆盖是基于运行时动态绑定的,而static方法是编译时静态绑定的,static方法跟类的任何实例都不相关,所以没有覆盖这个概念
java中也不可以覆盖private方法,因为private修饰的变量和方法只能在当前类中使用,任何其他类继承了该类是访问不到private变量和方法的
类加载机制:JVM把类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被JVM直接使用的java类型
双亲委派模型:每次收到类的加载请求时,先将请求委派给父类加载器,如果父类加载器无法完成加载,那么子类尝试自己加载,这样使得java类随着类加载器一起具备了一种带有优先级的层次关系
static修饰的,位于全局区
泛型是一种参数化的类型,它的<>里面可以放任何类型,而且不需要强制类型转化,是多态的一种表现
20.解释extends 和super 泛型限定符-上界不存下界不取
extends指定上界限,只能传入本类和子类
super指定下界限,只能传入本类和父类
不可以,因为静态的成员属于类,随着类的加载而加载到静态方法区内存,当内加载时,此时不一定有实例被创建,没有实例,就不能访问非静态的成员
1.通过class字节码对象newInstance()(默认通过无参构造创建)
2.通过获取构造器(有参构造器)
抽象类是对类的抽象,是一种模板设计,接口是行为的抽象,是一种行为规范(接口只能定义一系列方法算是定义行为,而不能包含具体的变量,不能拥有自己的属性,但是抽象类能够拥有)
接口是公开的,里面不能有私有的方法或变量,是让别人使用的,而抽象类可以有私有的方法或者变量,另外,实现接口的一定要实现接口定义的所有方法,而实现抽象类可以有选择的重写需要用得到的方法,一般应用开发,最顶级的是接口,然后是抽象类实现接口,最后才到具体类,还要接口可以实现多重继承,而一个类只能继承一个父类,但可以通过多个接口实现多重继承
24.Comparable和Comparator接口是干什么的?列出它们的区别。
都是比较器,用来实现对象的排序比较
comparable是内部比较比较器,简单的说就是把比较器写在类的内部,具体操作就是类实现comparable接口,然后重写compareto方法
comparato是外部比较器,简单的说就是把比较器写在类的外部(没错,就是在外边重新定义了一个比较器类),具体操作就是实现comparator接口,重写compare方法
内部比较器compareable比较符合java封装的思想,高内聚,但是!外部比较器比内部比较器灵活,更加容易维护!
25.final, finally, finalize的区别
final:用来定义变量,方法,参数,类,表示不可更改类型
finally:在try/catch语句中使用,附带一个语句块,表示最后执行
finalize:object的方法,垃圾回收器操作机制中的一部分,进行垃圾回收操作时会调用finalize,看重写这个方法,在这里实现释放系统资源等操作
26.Static Nested Class 和 Inner Class的不同
static nested class:静态内部类
inner class:内部类
静态内部类static nested class可以不依赖外部类的实例被实例化,而通常的内部类需要在外部类实例化之后才能实例化
内部类可以在两个地方定义:1.定义方法的地方,2.方法内
27.JAVA语言如何进行异常处理,关键字:throws,throw,try,catch,finally分别代表什么意义?在try块中可以抛出异常吗?
try:将可能发生异常的语句监视起来
catch:捕获到了异常就进行里面的操作
finall:不管是否有异常最后都会执行的语句
throws:抛出的异常的声明关键字
throw:抛出异常的动作的关键字
try块中不能抛出异常
如果不是静态内部类的话就可以,没有什么限制
四种内部类:
1.静态内部类:static修饰,只能访问外部类中被static修饰的成员变量或方法
2.成员内部类:最普通的内部类,可以无条件访问外部类的属性和方法
3.局部内部类:定义在外部类的方法中的,可以访问外部类的所有变量和方法,但是不能随便访问该方法里面的局部变量,除非该变量被final修饰
4.匿名内部类
https://www.cnblogs.com/yinbiao/p/10535976.html
30.Java中,什么是构造函数?什么是构造函数重载?什么是复制构造函数?
java中的构造函数是为了初始化对象的,构造函数的函数名和类名一致,默认的构造函数是没有参数,没有返回值的,且没有内容,构造函数的重载就是函数名和类名相同,参数类型不同,参数不同,同样也是初始化对象,java中没有复制构造函数的概念
Map是接口,concurrentHashMap是线程安全的map,具有同步能力
HashMap是数组加链表实现的,数组部分是主体,数组中的每一个部分可以是链表点,链表主要是为了解决hash冲突,HashMap根据键生成hash值,根据hash值存放键对应的值,而有时候可能两个不同的键hash值是相同的,会产生hash冲突,这个时候就需要采用链表存放产生hash冲突的值了
重写hashcode,最大程度避免产生hash冲突,还要重写equals
hashcode%length=键对应的值存放的位置
length:HashMap的容量=2^n
h:插入元素的hashcode
如果lenght为2^n,则转化为2进制必定是1111……的形式,这样在插入元素的hashcode与操作的时候效率会非常高,而且空间不浪费,如果length不是2^n,比如lenght=15,则length-1=14,对应的二进制为1110,那么与出的结果肯定是最后一位都为0,那么最后一位都为1的位置都被浪费了,比如0001,0101等等,这样使得空间浪费,增加了碰撞的概率
所以:当length=2^n时,h&(length-1)==h%length
HashTable是通过synchoronized来保证线程安全的,在总体上只加了一个锁,这样导致多线程时HashTable效率低下,一位当一个线程访问hashtable的同步方法时,其他线程要访问hashtable的同步方法会陷入阻塞,比如线程1使用put添加元素,线程2不能不能put添加元素,并且不能使用get获取元素,所以效率低下,原因就是HashTable只有一个整体锁,锁的粒读太大
ConcurrentHashMap使用分段锁技术,对每个段进行加锁,当线程1put元素时,并不是对整个HashMap进行加锁,而是通过hashcode值确定要将新值放入哪个分段,然后对那分段进行加锁,线程2只要pu的元素不是和线程1put的元素处于同一个分段,就实现了真正的并行插入!!(默认16个段)
细化锁的粒读!!!(分段锁技术)
一个segment元素代表一个分段锁
36.concurrenthashmap有啥优势,1.7,1.8区别?
在多线程环境下,使用HashMap进行put操作时存在数据丢失的情况,为了避免这种情况,可以使用多线程安全的concurrenthashmap代替HahsMap
在1.7下,concurrenthashmap采用segment+HashEntry实现,即分段锁+哈希表实现,哈希表是采用哈希数组和链表实现的,值得探讨的是concurrenthashmap的size方法的实现,计算元素的个数是个有趣的问题尤其是在多线程的环境下,因为你不知道你计算size的时候,是否有新的元素插入进来,JDK1.7对解决这个问题有两种解决方案:
第一个是不使用加锁的模式尝试多次去计算currenthashmap的size,最多三次,比较前后计算结果是否不同,结果一致就认为当前没有元素加入,计算结果是准确的,
第二种方法是如果第一种方法不符合,计就给每个segment加上锁,然后计算currentmap的size。
在1.8下,其已经摈弃了segment段的概念,而是直接用node数组+链表+红黑树的数据结构来实现,并发控制使用synchorized和CAS来操作,整个看起来就像是优化过且线程安全的HashMap,虽然在1.8中还能看到segment这种数据结构,但是已经简化了属性,只是为了兼容旧的版本,concurrenthashmap的初始化其实是一个空操作,这也是跟其他集合类有区别的地方,concurrenthashmap的初始化操作不是在构造函数实现的,而是在put操作中实现的,同时使用了红黑树来优化,因为基于长度很长的链表的遍历是一个很长的过程,而红黑树遍历的效率是很快的,代替一定阀值的链表,这是最佳的拍档
37.ArrayList和LinkedList的区别,如果一直在list的尾部添加元素,用哪个效率高?
Arraylist:可以理解为动态数组,底层实现仍是数组
LinkedList:单向链表
当一直在list尾部增加元素时,读写效率都是ArrayList高,但是当在指定位置插入的时候(最后情况每次都是插入在第一个的前面),LinkedList效率高
会越界,虽然ArrayList的底层是动态数组,但是在多线程的环境下,扩容的时候往往会发生越界,比如这样一种情况:
用第一次异常,即下标为15时的异常举例。当集合中已经添加了14个元素时,一个线程率先进入add()方法,在执行ensureCapacityInternal(size + 1)时,发现还可以添加一个元素,故数组没有扩容,但随后该线程被阻塞在此处。接着另一线程进入add()方法,执行ensureCapacityInternal(size + 1),由于前一个线程并没有添加元素,故size依然为14,依然不需要扩容,所以该线程就开始添加元素,使得size++,变为15,数组已经满了。而刚刚阻塞在elementData[size++] = e;语句之前的线程开始执行,它要在集合中添加第16个元素,而数组容量只有15个,所以就发生了数组下标越界异常!
39.什么是TreeMap?TreeMap的底层?
实现了sortMap接口,能够把保持的记录按照键排序(默认升序),也可以知道排序比较器,遍历时得到的数据是排过序的
红黑树
总共有两大接口,collection和Map,一个元素集合,一个键值对集合,其中List和Set接口继承了Collection接口,而ArrayList和LiskList实现了List接口,HashSet实现了Set接口,HashMap和HashTable实现了Map接口,并且HashTable是线程安全的,但是HashMap性能更好
Collecttion接口:
List接口:ArrayList,LinkList,Vector(Stack继承了Vector接口)
Set接口:HashSet,SortedSet(TreeSet)
Map接口:
SortedMap(TreeMap)
HashTable(LinkedHashMap)
WeakHashMap
41.为什么集合类没有实现Cloneable和Serializable接口?
Cloneable接口是用于浅克隆,而Serializable接口则是用于深克隆,标识性接口,之所以用到克隆是因为有时需要把对象信息保存到本地磁盘,防止在传输时出现乱序,而容器没有那个必要,只是用来存储数据而已
42.Iterator和ListIterator的区别是什么?
43.快速失败(fail-fast)和安全失败(fail-safe)的区别是什么?
快速失败:当迭代器遍历集合对象时,如果对集合对象的内容进行的修改,那么就会发生快速失败
原理:迭代器在遍历时直接访问集合中的内容,并且在遍历过程中使用一个modcount变量,集合在遍历期间如果内容发生变化,就会改变modcount的值,每当迭代器使用hashnext遍历下一元素之前,都会检测一下modcount的值,是的话返回遍历结果,不是的话抛出异常,终止遍历
快速失败不能在多线程下并发使用!
安全失败:采用安全失败机制的集合容器,在遍历时不能直接在集合的内容上访问,而是先复制原有的集合内容,在拷贝的集合上进行遍历,由于是对拷贝的进行遍历,所以在遍历过程中修改副本集合元素并不能被原集合检测到
安全失败可以在多线程先并发使用!
44.ArrayList,Vector,LinkedList的存储性能和特性是什么?
Vector使用了sychronized方法(线程安全),所以在性能上比ArrayList要差些.
LinkedList使用双向链表方式存储数据,按序号索引数据需要前向或后向遍历数据,所以索引数据慢,是插入数据时只需要记录前后项即可,所以插入的速度快。
45.Collection 和 Collections的区别。
Collection是接口,各自集合结构的父接口,继承它的接口主要有set,List,提供关于集合的一些操作
Collections是类,针对集合类的工具类,提供一系列的静态方法实现对各种集合的排序,搜索和线程安全
使用线程池(任务的提交和执行解耦)
优点:
1)降低资源消耗:通过重复利用已创建的线程来降低线程创建和销毁造成的消耗
2)提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行
3)提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控
线程池的运行流程:
线程池接受提交的任务,判断有没有空闲线程,有的话直接使用空闲线程执行任务,没有空闲线程的话,再判断当前线程数是否小于核心线程数,如果当前线程数小于核心线程数就创建一个新线程去执行该任务,如果线程数大于或等于核心线程数,就判断阻塞队列是否已满,阻塞队列没有满的话将任务加入队列等待执行,若阻塞队列满了的话,则判断当前线程数和最大线程数的关系,若大于等于则拒绝任务,若小于则创建新线程执行任务
线程池的参数:
1.corePoolSize:核心线程数
*核心线程会一直存活
*当线程数小于核心线程数时,即使有空闲线程,线程池也会优先创建新线程处理任务
*通过设置参数,核心线程也可以空闲超时关闭
2.queueCapacity:阻塞队列
*当核心线程数达到最大时,新任务会放在阻塞队列中等待
3.maxPoolsize:最大线程数
*当线程数>=核心线程数且阻塞队列已满,线程池会创建新线程来处理任务
*当前线程数=maxPoolSize且阻塞队列已满,线程池会拒绝任务抛出异常
4.keepAliveTime:线程空闲时间
*当线程空闲时间达到keepAliveTime时,线程会退出,直到线程数量=核心线程数
5.rejectedExecytionHandler:任务拒绝处理器
*两种情况会拒绝任务
*当前线程数已经达到最大线程数,阻塞队列已满,会拒绝新任务
*当线程池被调用shutdown后,会等待线程池里面的任务执行完毕再shutdown,如果在调用shutdown和线程池真正shutdown之间提交任务,会拒绝新任务
线程池的拒绝策略(4种):
1.丢弃任务并且抛出异常
2.丢弃任务不抛出异常
3.丢弃队列最前面的任务,然后尝试重新执行任务
4.由调用线程处理该任务
AQS:抽象的队列式同步器,是除了java自带的关键字之外的锁机制
AQS的核心思想:如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效线程,并将共享资源设置设置为锁定状态,如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时分锁分配的机制,这个机制就是AQS,AQS是用CLH自旋队列实现的,即将暂时不获取锁的线程加入到队列中
AQS是将每一条请求共享资源的线程封装成一个CLH锁队列的阶段,来实现锁的分配
用大白话说就是AQS是基于CLH队列,用volatile修饰共享变量state,线程通过CAS去改变状态符,成功则获取锁,失败则进入等待队列,等待被唤醒
AQS定义了两种资源共享方式:
*独占:只能一个线程执行
*共享:多个线程可以同时执行
1)继承类:继承Thread类,重写run方法
2)实现接口:实现Runnable接口,重写run方法
3)实现接口:实现Callable接口,重写run方法
4)使用Executor框架创建线程
一般使用实现Runnable接口的方法,因为一个类可以实现多个接口
1.newCachedThreadPool:线程数量不定的可缓存线程池,最大线程数是一个很大的值,空闲线程超时60秒被回收,该线程池比较适合大量但是耗时少的任务
2.newFixedThreadPool:指定工作线程数量的线程池,每当提交一个任务就创建一个工作线程,当工作线程数量到达线程池初始最大数,则将任务存入阻塞队列中,具有线程池提供程序效率和创建线程所耗开销的优点,但是在线程空闲时,即线程池中没有最终运行的任务,它也不会释放线程,会占用一定的系统资源
3.newSingleThreadExecutor:单线程化的Excetor,只创建唯一的工作线程来执行任务,保证所有任务按照FIFO执行,如果这个线程异常结束,会有另外一个线程取代它
4.newScheduleThreadPool:定长线程池,核心线程数量固定,而非核心线程数是没有限制的,并且当非核心线程闲置时间会被立刻回收,可安排给定延迟后运行命令或者定期执行,这类线程池主要用于执行定时任务和具有固定周期的重复任务
1)降低资源消耗:通过重复利用已经创建的线程来降低线程创建和销毁造成的消耗
2)提高响应速度:当任务到达时,任务可以不需要等待线程创建就能立即执行
3)提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使线程池可以进行统一的分配,调优和监控
52.cyclicbarrier和countdownlatch的区别
CountDownLatch:同步辅助类,允许一个线程或多个线程,等待其他一组线程完成操作,再继续执行,可以理解成倒计时锁,形象比喻:我们在玩LOL的时候会出现十个人不同的加载状态,但是最后一个人由于各种原因始终加载不了100%,于是系统自动等待玩家都加载为100%,才展现游戏画面
CyclicBarrier:可以看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍,CyclicBarrier就是一个栅栏,等待所有线程到达后再执行相关操作
java模块调用可以分为3个部分:同步调用,异步调用,回调
同步调用:类A的方法a调用类B的方法b,缺点就是如果类B的方法b在调用时阻塞了的话,类A的方法a剩余的代码无法继续执行下去
异步调用:类A的方法a通过新建线程的方式调用类B的方法b,就算类B的方法b阻塞了,类A的方法a也可以继续执行
回调:类A的a方法调用类B的b方法,类B的b方法执行完毕主动调用类A的callback方法,回调又分为同步回调和异步回调,同步回调意味着需要等待,异步回调意味着不需要等待
1)新建
2)就绪(可运行)
3)运行中
4)阻塞:等待阻塞(wait方法),同步阻塞(锁机制),其他阻塞(sleep方法,join方法,IO请求)
5)死亡
同步方法:修饰一般方法锁住的是当前对象的该方法,修饰静态方法是把当前类的所有实例对象都锁住了
同步代码块:指定获得哪个对象上的锁,锁住的是当前对象的某个代码块
两者的本质都是同步锁,不过是锁的粒度大小有区别,同步方法的锁粒度大,同步代码块的锁粒度小,同步代码块比同步方法好,因为我们一般都只需要对临界区进行同步,锁粒读越小,性能越高,而且同步代码块中我们可以自由的选择锁
56.在监视器(Monitor)内部,是如何做线程同步的?程序应该做哪种级别的同步?
监视器Monitor是一个同步工具,相当于操作系统中的互斥量,即值为1的信号量,它内置与每一个Object对象中,相当于一个许可证,拿到许可证就可以进行操作,没有拿到则需要阻塞等待
1.来源不同:sleep来自Thread类,而wait来自Object类,sleep是Thread的静态类方法,谁调用谁去睡觉,即使在线程a里面调用了b的sleep方法,还是线程a去睡觉,要让b去睡觉,得在b的代码中调用sleep
2.对资源的态度不同:sleep方法让线程去睡觉没有释放锁,没有让出资源,而wait释放了锁,进入线程等待池等待,一般wait不会加时间限制,因为如果wait运行的资源不够,出来也没有用,要等待其他线程调用notify或者allnotify方法唤醒等待池中的线程,才能进入就绪队列等待OS分配资源,sleep可以用指定的时间唤醒,如果时间没有到只能调用interrupt强行打断(Thread.sleep(0)的目的是触发操作系统重新进行一次CPU竞争
3.使用范围:sleep可以在任何地方使用,而wait只能在同步方法或者同步块中使用
4.异常处理:sleep必须捕获异常,而wait不用
1)synchotized同步方法或者同步方法块
2)wait和sleep
3)volatile变量
*每次volatile变量被修改后都会重新写入内存
*volatile变量为域变量的访问提供了一套免锁机制
*使用volatile修饰相当于告诉JVM该变量可能会被其他线程更新
*因此使用该变量每次都是重新从内存里面拿值,而不是拿寄存器里面的原来的值
*volatile不提供任何原子操作,它也不能用来修饰final类型的变量
4)使用可重入锁,ReenRantLock类,提供了可重入,互斥,实现了LOck接口的锁
5)局部变量 ThreadLocal类关联变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意改变自己的变量副本,而不会对其他线程产生影响
6)阻塞队列实现线程同步
7)使用原子变量实现线程同步:AtomicInteger类
59.stop()和suspend()方法为何不推荐使用,请说明原因?
stop会导致不安全,如果在同步块执行一半时,stop来了,后面还没有执行完呢,锁没了,线程退出了,别的线程又可以操作你的数据了,所以不安全
suspend会导致死锁,因为挂起后是不释放锁的,别人也就阻塞着,如果没有人唤醒,那就一直死锁
60.线程的sleep()方法和yield()方法有什么区别?
1)sleep方法给其他线程运行机会时不会考虑线程的优先级,因此会给低优先级的线程以运行的机会,yield方法只会给相同优先级或者更高优先级的线程机会
2)线程执行sleep后进入阻塞状态,而执行yield后进入就绪状态
3)sleep方法声明抛出InterruptedExcetion,而yield方法没有声明任何异常
对象监视器,会在对象头部有个区域,专门记录锁的信息,包括持续线程的锁,锁的计数器,锁的状态这些,线程在尝试获取对象锁时,先看看锁计数器是否为0,为0说明锁还在可以获取锁,于是获取锁,锁计数器变成1,并记录下持有锁的线程,当有线程再来请求时,先看看是不是当前持有锁的线程,是的那就直接访问,锁计数器+1,如果不是,直接阻塞,当退出时,计数器-1,变成0时,释放锁
1)Lock是一个接口,而synchorized是一个关键字
2)synchorized是内置语言实现的,是在JVM的层面实现的,可以通过工具进行监控,在dai吗异常时,JVM还会自动释锁定,而Lock不行,lock是通过代码实现的,只能程序员手动unlock,unlock必须写在finally{}中
3)Lock可以让等待的线程响应中断,线程可以去做其他事情,Synchorized不行,等待的线程会一直等待下去,不能去做其他事情
4)Lock可以知道是否获取到锁,知道锁的状态,而synchronized却无法办到
5)Lock可以提高多个线程进行读操作的效率
63.Java中的LongAdder和AtomicLong有什么区别?
AtomicLong的原理是依靠底层的CAS来保障原子性的更新数据,在要添加或者减少的时候会使用自循环(CLH)的方式不断的CAS到特定的值,从而达到更新数据的目的,然而在线程竞争激烈的情况下,自循环往往浪费很大的资源
所以面对AtomicLong的这个缺点,有了LongAdder的诞生,主要思想就是把一个数拆分成多个数的和,修改的时候只修改其中一个数,这样冲突的概率减少很多,有人说,base和数组中的值一直在该,会不准,不过这个没有关系,高并发的时候也不可能准,反正看到的都是瞬时的
1.反射机制的主要功能:
在运行时构造一个类的对象,判断一个类所具有的成员变量和方法,调用一个对象的方法,生成动态代理,反射最大的应用就是框架
2.反射的应用:
Spring的IOC/di
JavaBean和jsp之间的调用
JDBC的classForName
3.反射的缺点:性能问题,大量使用反射会使得系统性能大打折扣
4.基本反射功能的实现:
获取class对象:
*获取class类的ForName静态方法
*直接获取某一个对象的class
*调用某个对象的getClass方法
判断是否为某个类的实例:instanceof关键字判断是否为某个类的实例
创建实例:
*使用Class对象的newInstance方法来创建CLass对象对应类的实例
*先通过Class对象获取指定的Consructor对象,再调用Constructor对象的newInstance方法创建实例
*增大Xmx(程序最大可以从操作系统获取的内存数量)或者减少Xmn(年轻代大小)
*在应用访问最低的时候,在程序中主动调用system.gc(),比如每天凌晨
*在应用启动并完成所有初始化工作后,主动调用system.gc(),它可以将初始化数据压缩到一个单独的块中,以腾出更多的连续空间给新生代晋升使用
*降低XX:CMSInitiatingOccupancyFraction参数以提早执行垃圾回收,虽然不会进行内存碎片的压缩整理,但是它会合并老年代中相邻的free空间,这样可以容纳更多的新生代晋升行为
-XX:CMSInitiatingOccupancyFraction=70
CMS垃圾收集器,当老年代达到70%时,触发CMS垃圾回收。
前言:一个java文件从解码到运行一般包括两个过程:编译和运行,编译就通过javac命令编译成字节码文件,运行就是把class文件交给JVM执行,而我们所说的类加载过程就是JVM把字节码文件中类的信息加载进内存,并解析生成相应class对象的过程,JVM不是一开始就是把所有的类都加载进内存,而且只有第一次遇到某个需要运行的类才会加载而且只加载一次
类的加载过程:
1)加载:把字节码文件通过各个来源通过类加载器加载进入内存
*字节码来源:本地编译生成的字节码文件,jar包中的字节码文件,远程网络得到的字节码文件,以及动态代理实时编译的字节码文件
*类加载器:启动类加载器,扩展类加载器,应用类加载器,自定义类加载器
*为什么会有自定义类加载器?java代码容易被反编译,如果要对自己的代码加密的话,可以对编译后的代码进行加密,然后通过实现自己的自定义类加载器进行解密,最后再加载
2)验证:保证加载进来的字节流符合JVM规范,不会造成安全错误
*对文件格式的验证,比如常量中是否有不被支持的常量
*元数据的验证:类是否继承了被final修饰的类,类中的字段方法是否域父类冲突
*字节码验证:保证程序语义的合理性
*对符合引用的验证:比如检验符号引用中通过全限定名是否能够找到对应的类
3)准备:为类变量(不是实例变量)分配内存,赋予初值(8中类型基本数据默认0)
4)解析:将常量池符号引用替换为直接引用,举例说明:现在调用方法a,方法a的地址是1234567,那么a就是符号引用,1234567就是直接引用
5)初始化:对类变量初始化,是执行类构造器的过程,换句话说就是指对static修饰的变量或者语句进行初始化,如果初始化一个类,其父类尚未初始化,则优先初始化其父类,如果同时包含多个静态变量和代码块,则按照自上而下的顺序依次执行
类加载过程内存图:
这个如果不使用-xx:Xmx -xx:Xms -xx:Permsize -xx:MaxPermsize参数进行设置的话,应该和不同版本的jdk的jvm最大内存限制相关吧。
公司 JVM版本 最大内存(兆)client 最大内存(兆)server
SUN 1.5.x 1492 1520
SUN 1.5.5(Linux) 2634 2660
SUN 1.4.2 1564 1564
SUN 1.4.2(Linux) 1900 1260
IBM 1.4.2(Linux) 2047 N/A
BEA JRockit 1.5 (U3) 1909 1902
1.定义不同
运行时异常都是RunTimeException类及其子类,一般异常是RunTimeException以外的异常,都是Exception的子类
2.处理方法不同
运行时异常是不检查异常,程序可以捕获处理也可以不处理,一般异常必须进行捕获处理,否则不能编译通过
3.发生原因不同
运行时异常一般由程序逻辑错误引起
空指针异常:NullPointerException
类转换异常:ClassCastException
算术异常:ArithmeticExcepion
索引超出异常:IndexOutOfBoundsException
非法数据异常:LLLegalArgumentException
70.java中有几种类型的流?JDK为每种类型提供了一些抽象类以供继承,请分别说出他们是哪些类?
字节流,字符流
四种抽象类:字节流:InputStream,OutputStream
字符流:InputStreamReader,OutputStreamWriter
java序列化是一种处理java对象的流机制,即是将对象进行流化,流化后的对象可以进行读写操作,可以保持于本地,也可以用于网络传输
要实现java序列化,需要被序列化的类实现Serializable,通过使用输出流(如FileOutputStream),可以构造对象输出流,并使用其writeObject方法将对象写出,可以通过构造对象输入流接收对象