1.线程有几种状态产生的条件是什么新建状态(New) 线程对象被创建后就进入了新建状态。例如Thread thread new Thread()。2就绪状态(Runnable): 也被称为“可执行状态”。线程对象被创建后其它线程调用了该对象的start()方法从而来启动该线程。例如thread.start()。处于就绪状态的线程随时可能被CPU调度执行。3运行状态(Running)线程获取CPU权限进行执行。需要注意的是线程只能从就绪状态进入到运行状态。4阻塞状态(Blocked)阻塞状态是线程因为某种原因放弃CPU使用权暂时停止运行。直到线程进入就绪状态才有机会转到运行状态。阻塞的情况分三种等待阻塞 -- 通过调用线程的wait()方法让线程等待某工作的完成。同步阻塞 -- 线程在获取synchronized同步锁失败(因为锁被其它线程所占用)它会进入同步阻塞状态。其他阻塞 -- 通过调用线程的sleep()或join()或发出了I/O请求时线程会进入到阻塞状态。当sleep()状态超时、join(等待线程终止或者超时、或者I/O处理完毕时线程重新转入就绪状态。5死亡状态(Dead)线程执行完了或者因异常退出了run()方法该线程结束生命周期。2.产生死锁的基本条件产生死锁的原因1 因为系统资源不足。2 进程运行推进的顺序不合适。3 资源分配不当等。如果系统资源充足进程的资源请求都能够得到满足死锁出现的可能性就很低否则就会因争夺有限的资源而陷入死锁。其次进程运行推进顺序与速度不同也可能产生死锁。产生死锁的四个必要条件1 互斥条件一个资源每次只能被一个进程使用。2 请求与保持条件一个进程因请求资源而阻塞时对已获得的资源保持不放。3 不剥夺条件:进程已获得的资源在末使用完之前不能强行剥夺。4 循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系。这四个条件是死锁的必要条件只要系统发生死锁这些条件必然成立而只要上述条件之一不满足就不会发生死锁。死锁的解除与预防理解了死锁的原因尤其是产生死锁的四个必要条件就可以最大可能地避免、预防和解除死锁。所以在系统设计、进程调度等方面注意如何不让这四个必要条件成立如何确定资源的合理分配算法避免进程永久占据系统资源。此外也要防止进程在处于等待状态的情况下占用资源。因此对资源的分配要给予合理的规划。3.什么是线程池如何使用线程池就是事先将多个线程对象放到一个容器中当使用的时候就不用 new 线程而是直接去池中拿线程即可节省了开辟子线程的时间提高的代码执行效率。在 JDK 的 java.util.concurrent.Executors 中提供了生成多种线程池的静态方法。ExecutorService newCachedThreadPool Executors.newCachedThreadPool();ExecutorService newFixedThreadPool Executors.newFixedThreadPool(4);ScheduledExecutorService newScheduledThreadPool Executors.newScheduledThreadPool(4);ExecutorService newSingleThreadExecutor Executors.newSingleThreadExecutor();然后调用他们的 execute 方法即可。优点第一降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二提高响应速度。当任务到达时任务可以不需要等到线程创建就能立即执行。第三提高线程的可管理性。线程是稀缺资源如果无限制的创建不仅会消耗系统资源还会降低系统的稳定性使用线程池可以进行统一的分配调优和监控。4.Java自带有哪几种线程池1newCachedThreadPool创建一个可缓存线程池如果线程池长度超过处理需要可灵活回收空闲线程若无可回收则新建线程。这种类型的线程池特点是工作线程的创建数量几乎没有限制(其实也有限制的,数目为Interger. MAX_VALUE), 这样可灵活的往线程池中添加线程。如果长时间没有往线程池中提交任务即如果工作线程空闲了指定的时间(默认为1分钟)则该工作线程将自动终止。终止后如果你又提交了新的任务则线程池重新创建一个工作线程。在使用CachedThreadPool时一定要注意控制任务的数量否则由于大量线程同时运行很有会造成系统瘫痪。2newFixedThreadPool创建一个指定工作线程数量的线程池。每当提交一个任务就创建一个工作线程如果工作线程数量达到线程池初始的最大数则将提交的任务存入到池队列中。FixedThreadPool是一个典型且优秀的线程池它具有线程池提高程序效率和节省创建线程时所耗的开销的优点。但是在线程池空闲时即线程池中没有可运行任务时它不会释放工作线程还会占用一定的系统资源。3newSingleThreadExecutor创建一个单线程化的Executor即只创建唯一的工作者线程来执行任务它只会用唯一的工作线程来执行任务保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。如果这个线程异常结束会有另一个取代它保证顺序执行。单工作线程最大的特点是可保证顺序地执行各个任务并且在任意给定的时间不会有多个线程是活动的。4newScheduleThreadPool创建一个定长的线程池而且支持定时的以及周期性的任务执行。例如延迟3秒执行。篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、场景题、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho5.Java 中有几种类型的流6.字节流如何转为字符流字节输入流转字符输入流通过 InputStreamReader 实现该类的构造函数可以传入 InputStream 对象。字节输出流转字符输出流通过OutputStreamWriter 实现该类的构造函数可以传入 OutputStream 对象。7.请写出你最常见的5个RuntimeException1java.lang.NullPointerException 空指针异常出现原因调用了未经初始化的对象或者是不存在的对象。2java.lang.ClassNotFoundException 指定的类找不到出现原因类的名称和路径加载错误通常都是程序试图通过字符串来加载某个类时可能引发异常。3java.lang.NumberFormatException 字符串转换为数字异常出现原因字符型数据中包含非数字型字符。4java.lang.IndexOutOfBoundsException 数组角标越界异常常见于操作数组对象时发生。5java.lang.IllegalArgumentException 方法传递参数错误。6java.lang.ClassCastException 数据类型转换异常。8.JDK和JRE有什么区别1JRE是Java运行时环境就是Java程序必须运行在JRE上面它就是执行Java程序的虚拟机。2JDK是包含JRE的它比JRE多了一些工具这些多出来的工具就是让我们程序员来开发Java程序的。3JDK包含JRE编译器和其他的工具(比如JavaDocJava调试器)可以让开发者开发、编译、执行Java应用程序4JDK面向开发者JRE面向程序使用者9.两个对象的 hashCode()相同则 equals()也一定为 true对吗1两个对象equals相等则它们的hashcode必须相等反之则不一定。2两个对象相等则其hashcode一定相等反之不一定成立10.final 在 java 中有什么作用final作为Java中的关键字可以用于三个地方。用于修饰类、类属性和类方法。特征凡是引用final关键字的地方皆不可修改(1)修饰类表示该类不能被继承(2)修饰方法表示方法不能被重写(3)修饰变量表示变量只能一次赋值以后值不能被修改常量。11.Java 中的 Math.round(-1.5) 等于多少Math.round(-1.5)的返回值是-1。四舍五入的原理是在参数上加0.5然后做向下取整12.String 属于基础的数据类型吗在Java中数据类型分为引用类型和基本类型基本类型分为八种整型byteshortint,long浮点型floatdouble字符型charBoolean型booleanString不是基本的数据类型是final修饰的java类是引用类型。13. Java 中操作字符串都有哪些类它们之间有什么区别主要是一下三种String、StringBuffer、StringBuilder特别是在项目中。先来看一下这三种操作方式的区别String是不可变的对象对每次对String类型的改变时都会生成一个新的对象StringBuffer和StringBuilder是可以改变对象的。对于操作效率StringBuilder StringBuffer String对于线程安全StringBuffer 是线程安全可用于多线程StringBuilder 是非线程安全用于单线程不频繁的字符串操作使用 String。反之StringBuffer 和 StringBuilder 都优于String所以如果在项目中需要拼接字符串最好是采用StringBuffer 而非String.14.String stri与 String strnew String(“i”)一样吗不一样因为内存的分配方式不一样。String stri的方式Java 虚拟机会将其分配到常量池中而 String strnew String(“i”) 则会被分到堆内存中。15.String 类的常用方法都有那些indexOf() 返回指定字符得索引charAt() 返回指定索引处得字符repalce() 字符串替换trim() 去除字符串两端的空白split() 分割字符串 返回分割后的字符串数组getBytes() 返回字符串的byte类型数组length() 返回字符串的长度toLowerCase() 字符串转小写toUpperCase() 字符串转大写substring() 截取字符串equals() 字符串比较16.抽象类必须要有抽象方法吗抽象类可以没有抽象方法但是如果你的一个类已经声明成了抽象类即使这个类中没有抽象方法它也不能再实例化即不能直接构造一个该类的对象。如果一个类中有了一个抽象方法那么这个类必须声明为抽象类否则编译通不过。17.普通类和抽象类有哪些区别普通类不能包含抽象方法抽象类可以包含抽象方法。抽象类是不能被实例化的就是不能用new调出构造方法创建对象普通类可以直接实例化。如果一个类继承于抽象类则该子类必须实现父类的抽象方法。如果子类没有实现父类的抽象方法则必须将子类也定义为abstract类。18.抽象类能使用 final 修饰吗不能定义抽象类就是让其他类继承的如果定义为 final 该类就不能被继承这样彼此就会产生矛盾所以 final 不能修饰抽象类如下图所示编辑器也会提示错误信息。19.接口和抽象类有什么区别他们都不能实例化对象都可以包含抽象方法而且抽象方法必须被继承的类全部实现。区别1、抽象类和接口都不能直接实例化如果要实例化抽象类变量必须指向实现所有抽象方法的子类对象接口变量必须指向实现所有接口方法的类对象。2、抽象类要被子类继承接口要被类实现。3、接口只能做方法申明抽象类中可以做方法申明也可以做方法实现4、接口里定义的变量只能是公共的静态的常量抽象类中的变量是普通变量。5、抽象类里的抽象方法必须全部被子类所实现如果子类不能全部实现父类抽象方法那么该子类只能是抽象类。同样一个实现接口的时候如不能全部实现接口方法那么该类也只能为抽象类。6、抽象方法只能申明不能实现接口是设计的结果 抽象类是重构的结果7、抽象类里可以没有抽象方法8、如果一个类里有抽象方法那么这个类只能是抽象类9、抽象方法要被实现所以不能是静态的也不能是私有的。10、接口可继承接口并可多继承接口但类只能单根继承。20.Java 中 IO 流分为几种按照流的流向分可以分为输入流和输出流按照操作单元划分可以划分为字节流和字符流按照流的角色划分为节点流和处理流。Java Io流共涉及40多个类这些类看上去很杂乱但实际上很有规则而且彼此之间存在非常紧密的联系 Java I0流的40多个类都是从如下4个抽象类基类中派生出来的。InputStream/Reader: 所有的输入流的基类前者是字节输入流后者是字符输入流。OutputStream/Writer: 所有输出流的基类前者是字节输出流后者是字符输出流。按操作方式分类结构图21.BIO、NIO、AIO 有什么区别1BIO在服务端启动一个ServerSocket然后在客户端启动Socket来对服务端进行通信默认情况下服务端需要对每个请求建立一堆线程等待请求而客户端发送请求后先咨询服务端是否有线程相应如果没有则会一直等待或者遭到拒绝请求如果有的话客户端会线程会等待请求结束后才继续执行。简单来说用户进程在发起一个IO操作以后必须等待IO操作的完成只有当真正完成了IO操作以后用户进程才能运行BIO方式适用于连接数目比较小且固定的架构。2NIONIO基于Reactor当socket有流可读或可写入socket时操作系统会相应的通知引用程序进行处理应用再将流读取到缓冲区或写入操作系统。 当一个连接创建后不需要对应一个线程这个连接会被注册到多路复用器上面所以所有的连接只需要一个线程就可以搞定当这个线程中的多路复用器进行轮询的时候发现连接上有请求的话才开启一个线程进行处理。简单来说用户进程发起一个IO操作以后边可返回做其它事情但是用户进程需要时不时的询问IO操作是否就绪引入不必要的CPU资源浪费NIO方式适用于连接数目多且连接比较短的架构。BIO的每个连接一个单独的线程而NIO则是每个连接共用一个线程。3AIO当有流可读取时操作系统会将可读的流传入read方法的缓冲区并通知应用程序对于写操作而言当操作系统将write方法传递的流写入完毕时操作系统主动通知应用程序。read/write方法都是异步的完成后会主动调用回调函数。 AIO是一个有效请求一个线程。简单来说用户进程只需要发起一个IO操作然后立即返回等IO操作真正的完成以后应用程序会得到IO操作完成的通知。不需要进行实际的IO读写操作因为真正的IO读取或者写入操作已经由内核完成了。AIO方式使用于连接数目多且连接比较长的架构。22.Files的常用方法都有哪些Files. exists()检测文件路径是否存在。Files. createFile()创建文件。Files. createDirectory()创建文件夹。Files. delete()删除一个文件或目录。Files. copy()复制文件。Files. move()移动文件。Files. size()查看文件个数。Files. read()读取文件。Files. write()写入文件。23.Java 容器都有哪些如图所示24.Collection 和 Collections 有什么区别java.util.Collection 是一个集合接口集合类的一个顶级接口。它提供了对集合对象进行基本操作的通用接口方法。Collection接口在Java 类库中有很多具体的实现。Collection接口的意义是为各种具体的集合提供了最大化的统一操作方式其直接继承接口有List与Set。Collections则是集合类的一个工具类/帮助类其中提供了一系列静态方法用于对集合中元素进行排序、搜索以及线程安全等各种操作。25.List、Set、Map 之间的区别是什么26.说一下 HashSet 的实现原理1基于HashMap实现的默认构造函数是构建一个初始容量为16负载因子为0.75 的HashMap。封装了一个 HashMap 对象来存储所有的集合元素所有放入 HashSet 中的集合元素实际上由 HashMap 的 key 来保存而 HashMap 的 value 则存储了一个 PRESENT它是一个静态的 Object 对象。2当我们试图把某个类的对象当成 HashMap的 key或试图将这个类的对象放入 HashSet 中保存时重写该类的equals(Object obj)方法和 hashCode() 方法很重要而且这两个方法的返回值必须保持一致当该类的两个的 hashCode() 返回值相同时它们通过 equals() 方法比较也应该返回 true。通常来说所有参与计算 hashCode() 返回值的关键属性都应该用于作为 equals() 比较的标准。3HashSet的其他操作都是基于HashMap的。篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、场景题、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho27.如何决定使用 HashMap 还是 TreeMap对于在Map中插入、删除和定位元素这类操作HashMap是最好的选择。然而假如你需要对一个有序的key集合进行遍历TreeMap是更好的选择。基于你的collection的大小也许向HashMap中添加元素会更快将map换为TreeMap进行有序key的遍历。28.如何实现数组和 List 之间的转换List转数组toArray(arraylist.size()方法数组转ListArrays的asList(a)方法3.42哪些集合类是线程安全的在集合框架中有些类是线程安全的这些都是jdk1.1中的出现的。在jdk1.2之后就出现许许多多非线程安全的类。 下面是这些线程安全的同步的类vector就比arraylist多了个同步化机制线程安全因为效率较低现在已经不太建议使用。在web应用中特别是前台页面往往效率页面响应速度是优先考虑的。statck堆栈类先进后出hashtable就比hashmap多了个线程安全enumeration枚举相当于迭代器StringBuffer也是线程安全的StringBuilder 不是线程安全的除了这些之外其他的都是非线程安全的类和接口。线程安全的类其方法是同步的每次只能一个访问。是重量级对象效率较低。3.43 Iterator 怎么使用有什么特点1.Iterator在遍历元素过程中有线程修改集合元素会有ConcurrentModificationEception异常2.Iterator本身不具有装载数据功能需依附Collection对象使用3.next是用游标指向的方式返回下一个元素的3.44Iterator 和 ListIterator 有什么区别我们需要知道的第一个则是1所属关系ListIterator是一个Iterator的子类型。2局限只能应用于各种List类的访问。3优势Iterator只能向前移动而ListIterator可以双向移动。还可以产生相对于迭代器在列表中指向的当前位置的前一个和后一个元素的索引nextIndex()、previousIndex()方法。还可以通过set方法替换它访问过的最后一个元素。还可以通过调用listIterator方法产生一个指向List开始处的ListIterator当然也可以有参数即指向索引为参数处的ListIterator。4ListIterator 有 add() 方法可以向 List 中添加对象而 Iterator 不能3.45创建线程有哪几种方式1.继承Thread类实现多线程2.覆写Runnable()接口实现多线程而后同样覆写run().推荐此方式3.覆写Callable接口实现多线程JDK1.54.通过线程池启动多线程3.46说一下 runnable 和 callable 有什么区别Runnable应该是比较熟悉的接口它只有一个run()函数用于将耗时操作写在其中该函数没有返回值不能将结果返回给客户程序。然后使用某个线程去执行runnable即可实现多线程Thread类在调用start()函数后就是执行的是Runnable的run()函数。Callable与Runnable的功能大致相似Callable中有一个call()函数但是call()函数有返回值。主要区别Runnable 接口 run 方法无返回值Callable 接口 call 方法有返回值支持泛型Runnable 接口 run 方法只能抛出运行时异常且无法捕获处理Callable 接口 call 方法允许抛出异常可以获取异常信息3.47sleep() 和 wait() 有什么区别sleep方法属于Thread类中的方法会导致程序暂停执行指定的时间让出cpu该其他线程但是他的监控状态依然保持着当指定时间到了之后又会自动恢复运行状态在调用sleep方法的过程中线程不会释放对象锁。只会让出CPU不会导致锁行为的改变wait方法属于Object类中的方法在调用wait方法的时候线程会放弃对象锁进入等待此对象的等待锁定池只有针对此对象调用notify方法后本线程才进入对象锁定池准备。获取对象锁进入运行状态。不仅让出CPU还释放已经占有的同步资源锁3.48notify()和 notifyAll()有什么区别notify():唤醒一个处于等待状态的线程注意的是在调用此方法的时候并不能确切的唤醒某一个等待状态的线程而是由JVM确定唤醒哪个线程而且不是按优先级。notifyAll():唤醒所有处入等待状态的线程;并可以理解为把他们排进一个队列;只不过只有头部的线程获得了锁才能运行;注意并不是给所有唤醒线程一个对象的锁而是让它们竞争当其中一个线程运行完就开始运行下一个已经被唤醒的线程因为锁已经转移了。3.49线程的 run()和 start()有什么区别run()方法是在主线程中执行方法和调用普通方法一样按顺序执行同步执行start()方法是创建了新的线程在新的线程中执行异步执行3.50线程池中 submit()和 execute()方法有什么区别execute() 参数 Runnable submit() 参数 (Runnable) 或 (Runnable 和 结果 T) 或 (Callable)execute() 没有返回值而 submit() 有返回值submit() 的返回值 Future 调用get方法时可以捕获处理异常3.51在 java 程序中怎么保证多线程的运行安全线程的安全性问题体现在原子性一个或者多个操作在 CPU 执行的过程中不被中断的特性可见性一个线程对共享变量的修改另外一个线程能够立刻看到有序性程序执行的顺序按照代码的先后顺序执行导致原因缓存导致的可见性问题线程切换带来的原子性问题编译优化带来的有序性问题解决办法JDK Atomic开头的原子类、synchronized、LOCK可以解决原子性问题synchronized、volatile、LOCK可以解决可见性问题Happens-Before 规则可以解决有序性问题Happens-Before 规则如下程序次序规则在一个线程内按照程序控制流顺序书写在前面的操作先行发生于书写在后面的操作管程锁定规则一个unlock操作先行发生于后面对同一个锁的lock操作volatile变量规则对一个volatile变量的写操作先行发生于后面对这个变量的读操作线程启动规则Thread对象的start()方法先行发生于此线程的每一个动作线程终止规则线程中的所有操作都先行发生于对此线程的终止检测线程中断规则对线程interrupt()方法的调用先行发生于被中断线程的代码检测到中断事件的发生对象终结规则一个对象的初始化完成(构造函数执行结束)先行发生于它的finalize()方法的开始3.52什么是死锁所谓死锁是指多个进程在运行过程中因争夺资源而造成的一种僵局当进程处于这种僵持状态时若无外力作用它们都将无法再向前推进。 因此我们举个例子来描述如果此时有一个线程A按照先锁a再获得锁b的的顺序获得锁而在此同时又有另外一个线程B按照先锁b再锁a的顺序获得锁。如下图所示3.53怎么防止死锁预防死锁资源一次性分配一次性分配所有资源这样就不会再有请求了破坏请求条件只要有一个资源得不到分配也不给这个进程分配其他的资源破坏请保持条件可剥夺资源即当某进程获得了部分资源但得不到其它资源则释放已占有的资源破坏不可剥夺条件资源有序分配法系统给每类资源赋予一个编号每一个进程按编号递增的顺序请求资源释放则相反破坏环路等待条件1、以确定的顺序获得锁如果必须获取多个锁那么在设计的时候需要充分考虑不同线程之前获得锁的顺序。按照上面的例子两个线程获得锁的时序图如下那么死锁就永远不会发生。 针对两个特定的锁开发者可以尝试按照锁对象的hashCode值大小的顺序分别获得两个锁这样锁总是会以特定的顺序获得锁那么死锁也不会发生。问题变得更加复杂一些如果此时有多个线程都在竞争不同的锁简单按照锁对象的hashCode进行排序单纯按照hashCode顺序排序会出现“环路等待”可能就无法满足要求了这个时候开发者可以使用银行家算法所有的锁都按照特定的顺序获取同样可以防止死锁的发生该算法在这里就不再赘述了有兴趣的可以自行了解一下。篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、场景题、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho2、超时放弃当使用synchronized关键词提供的内置锁时只要线程没有获得锁那么就会永远等待下去然而Lock接口提供了boolean tryLock(long time, TimeUnit unit) throws InterruptedException方法该方法可以按照固定时长等待锁因此线程可以在获取锁超时以后主动释放之前已经获得的所有的锁。通过这种方式也可以很有效地避免死锁。 还是按照之前的例子时序图如下避免死锁:预防死锁的几种策略会严重地损害系统性能。因此在避免死锁时要施加较弱的限制从而获得 较满意的系统性能。由于在避免死锁的策略中允许进程动态地申请资源。因而系统在进行资源分配之前预先计算资源分配的安全性。若此次分配不会导致系统进入不安全的状态则将资源分配给进程否则进程等待。其中最具有代表性的避免死锁算法是银行家算法。银行家算法首先需要定义状态和安全状态的概念。系统的状态是当前给进程分配的资源情况。因此状态包含两个向量Resource系统中每种资源的总量和Available未分配给进程的每种资源的总量及两个矩阵Claim表示进程对资源的需求和Allocation表示当前分配给进程的资源。安全状态是指至少有一个资源分配序列不会导致死锁。当进程请求一组资源时假设同意该请求从而改变了系统的状态然后确定其结果是否还处于安全状态。如果是同意这个请求如果不是阻塞该进程知道同意该请求后系统状态仍然是安全的。3.54 synchronized 和 volatile 的区别是什么1)volatile比synchronized更轻量级。2)volatile没有synchronized使用的广泛。3)volatile不需要加锁比synchronized更轻量级不会阻塞线程。4)从内存可见性角度看volatile读相当于加锁volatile写相当于解锁。5)synchronized既能保证可见性又能保证原子性而volatile只能保证可见性无法保证原子性。6)volatile本身不保证获取和设置操作的原子性仅仅保持修改的可见性。但是java的内存模型保证声明为volatile的long和double变量的get和set操作是原子的。3.55synchronized 和 Lock 有什么区别3.56 synchronized 和 ReentrantLock 区别是什么基本意义Synchronized是Java语言的关键字因此Synchronized的锁是原生语法层面的互斥需要JVM来实现。具体是通过对象内部的一个叫做监视器锁monitor来实现的。ReentrantLock字面意思可重入锁它是JDK1.5之后提供的API层面的互斥锁锁的功能主要由2个方法完成即lock()和unlock()。易用性Synchronized的使用比较方便简洁由编译器去保证锁的加锁和释放而ReentrantLock需要手动写代码来加锁和释放锁。注意为避免忘记手工释放锁而造成了死锁最好在finally中声明释放锁。灵活度ReentrantLock要优于Synchronized可以灵活控制在哪个位置加锁和解锁。性能区别我们平时写代码的时候用到Java最多的锁是Synchronized单例模式中看到的锁也是Synchronized。这是为什么呢除了Synchronized使用方面一些其实和ReentrantLock对比性能也丝毫不逊色这里面说的当然是JDK1.5以后的版本了。JDK1.6以前的版本没有优化这时和ReentrantLock比肯定差很多。优化以后的Synchronized引入了包括偏向锁轻量级锁等这样就和ReentrantLock性能差不多了。3.57什么是反射Java 反射机制是在运行状态中对于任意一个类都能够获得这个类的所有属性和方法对于任意一个对象都能够调用它的任意一个属性和方法。这种在运行时动态的获取信息以及动态调用对象的方法的功能称为Java 的反射机制。Class 类与java.lang.reflect 类库一起对反射的概念进行了支持该类库包含了Field,Method,Constructor类(每个类都实现了Member 接口)。这些类型的对象时由JVM 在运行时创建的用以表示未知类里对应的成员。这样就可以使用Constructor 创建新的对象用get() 和set() 方法读取和修改与Field 对象关联的字段用invoke() 方法调用与Method 对象关联的方法。另外还可以调用getFields() getMethods() 和 getConstructors() 等很便利的方法以返回表示字段方法以及构造器的对象的数组。这样匿名对象的信息就能在运行时被完全确定下来而在编译时不需要知道任何事情。3.58动态代理是什么有哪些应用动态代理是运行时动态生成代理类。 动态代理的应用有 spring aop、hibernate 数据查询、测试框架的后端 mock、rpcJava注解对象获取等。3.59深拷贝和浅拷贝区别是什么浅拷贝创建一个新对象然后将当前对象的非静态字段复制到该新对象如果字段是值类型的那么对该字段执行复制如果该字段是引用类型的话则复制引用但不复制引用的对象。因此原始对象及其副本引用同一个对象。深拷贝创建一个新对象然后将当前对象的非静态字段复制到该新对象无论该字段是值类型的还是引用类型都复制独立的一份。当你修改其中一个对象的任何内容时都不会影响另一个对象的内容。3.60throw 和 throws 的区别throw:(1)throw 语句用在方法体内表示抛出异常由方法体内的语句处理。(2)throw 是具体向外抛出异常的动作所以它抛出的是一个异常实例执行 throw 一定是抛出了某种异常。throws:(1)throws 语句是用在方法声明后面表示如果抛出异常由该方法的调用者来进行异常的处理。(2)throws 主要是声明这个方法会抛出某种类型的异常让它的使用者要知道需要捕获的异常的类型。(3)throws 表示出现异常的一种可能性并不一定会发生这种异常。3.61final、finally、finalize 有什么区别1、finalFinal可以用于成员变量包括方法参数方法、类。Final成员作为变量变量一旦被初始化便不可改变对于基本类型指的是值不变对于对象类型指的是引用不变初始化只可能在两个地方定义处和构造函数。作为方法参数对于基本类型定义成final参数没有什么意义因为基本类型就是传值不会影响调用语句中的变量对于对象类型在方法中如果参数确认不需要改变时定义成final参数可以防止方法中无意的修改而影响到调用方法。Final方法不可覆写编译器将对此方法的调用转化成行内inline调用即直接把方法主体插入到调用处方法主体内容过多的时候反而会影响效率Final类不可继承2、finally异常处理关键字finally中的主体总会执行不管异常发生是否。通常放在try…catch的后面构造最终执行代码块这就意味着程序无论正常执行还是发生异常这里的代码只要JVM不关闭都能执行可以将释放外部资源的代码写在finally块中。2.1、当try中有return时执行顺序return语句并不是函数的最终出口如果有finally语句这在return之后还会执行finallyreturn的值会暂存在栈里面等待finally执行后再返回2.2、return和异常获取语句的位置3、finalize类的Finalize方法可以告诉垃圾回收器应该执行的操作该方法从Object类继承而来。在从堆中永久删除对象之前垃圾回收器调用该对象的Finalize方法。注意无法确切地保证垃圾回收器何时调用该方法也无法保证调用不同对象的方法的顺序。即使一个对象包含另一个对象的引用或者在释放一个对象很久以前就释放了另一个对象也可能会以任意的顺序调用这两个对象的Finalize方法。如果必须保证采用特定的顺序则必须提供自己的特有清理方法。3.64 Java中CyclicBarrier 和 CountDownLatch有什么不同1、CyclicBarrier的某个线程运行到某个点后停止运行直到所有线程都达到同一个点所有线程才会重新运行CountDownLatch线程运行到某个点后计数值-1该线程继续运行直到计数值为0则停止运行2、CyclicBarrier只能唤醒一个任务CountDownLatch可以唤醒多个任务3、CyccliBarrier可以重用CountDownLatch不可重用当计数值为0时CountDownLatch就不可再用了。3.65为什么说 Synchronized 是一个悲观锁?乐观锁的实现原理 又是什么?什么是 CAS它有什么特性?Synchronized显然是一个悲观锁因为它的并发策略是悲观的不管是否会产生竞争任何的数据操作都必须要加锁、用户态核心态转换、维护锁计数器和检查是否有被阻塞的线程需要被唤醒等操作。随着硬件指令集的发展我们可以使用基于冲突检测的乐观并发策略。先进行操作如果没有其他线程征用数据那操作就成功了如果共享数据有征用产生了冲突那就再进行其他的补偿措施。这种乐观的并发策略的许多实现不需要线程挂起所以被称为非阻塞同步。乐观锁的核心算法是CASCompareandSwap比较并交换它涉及到三个操作数内存值、预期值、新值。当且仅当预期值和内存值相等时才将内存值修改为新值。这样处理的逻辑是首先检查某块内存的值是否跟之前我读取时的一样如不一样则表示期间此内存值已经被别的线程更改过舍弃本次操作否则说明期间没有其他线程对此内存值操作可以把新值设置给此块内存。CAS具有原子性它的原子性由CPU硬件指令实现保证即使用JNI调用Native方法调用由C编写的硬件级别指令JDK中提供了Unsafe类执行这些操作。3.66 Java中Semaphore是什么Java中的Semaphore是一种新的同步类它是一个计数信号。从概念上讲从概念上讲信号量维护了一个许可集合。如有必要在许可可用前会阻塞每一个 acquire()然后再获取该许可。每个 release()添加一个许可从而可能释放一个正在阻塞的获取者。但是不使用实际的许可对象Semaphore只对可用许可的号码进行计数并采取相应的行动。信号量常常用于多线程的代码中比如数据库连接池。3.67现在有T1、T2、T3三个线程你怎样保证T2在T1执行完后执行T3在T2执行完后执行篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了Java面试、场景题、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc需要全套面试笔记及答案【点击此处即可/免费获取】https://docs.qq.com/doc/DQXdYWE9LZ2ZHZ1ho在多线程中有多种方法让线程按特定顺序执行你可以用线程类的join()方法在一个线程中启动另一个线程另外一个线程完成该线程继续执行。为了确保三个线程的顺序你应该先启动最后一个(T3调用T2T2调用T1)这样T1就会先完成而T3最后完成。3.68你如何在Java中获取线程堆栈Java虚拟机提供了线程转储thread dump的后门通过这个后门可以把线程堆栈打印出来。通常我们将堆栈信息重定向到一个文件中便于我们分析由于信息量太大很可能超出控制台缓冲区的最大行数限制造成信息丢失。这里介绍一个jdk自带的打印线程堆栈的工具jstack用于打印出给定的Java进程ID或core file或远程调试服务的Java堆栈信息。示例$jstack –l 23561 xxx.dump命令 : $jstack [option] pid 文件表示输出到文件尾部实际运行中往往一次dump的信息还不足以确认问题建议产生三次dump信息如果每次dump都指向同一个问题我们才确定问题。3.69提交任务时线程池队列已满会时发会生什么这个问题问得很狡猾许多程序员会认为该任务会阻塞直到线程池队列有空位。但是最大线程数没有满的话就会新建一个非核心线程去执行该任务。如果核心线程数、阻塞队列、最大线程数都满了的话就会执行线程池的拒绝策略如果一个任务不能被调度执行那么ThreadPoolExecutor’s submit()方法将会抛出一个RejectedExecutionException异常3.70什么是乐观锁和悲观锁乐观锁总是认为不会产生并发问题每次去取数据的时候总认为不会有其他线程对数据进行修改因此不会上锁但是在更新时会判断其他线程在这之前有没有对数据进行修改一般会使用版本号机制或CAS操作实现。乐观锁适用于多读的应用类型这样可以提高吞吐量像数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。悲观锁顾名思义就是很悲观总是假设最坏的情况每次取数据时都认为其他线程会修改所以都会加锁读锁、写锁、行锁等当其他线程想要访问数据时都需要阻塞挂起。可以依靠数据库实现如行锁、读锁和写锁等都是在操作之前加锁在Java中synchronized的思想也是悲观锁。