服务热线
0574-55011290
| 数据库&事务 1.什么样的场景会产生数据库死锁,如何解决? 2.SQL如何优化 3.Oracle和mysql分页的实现原理 4.唯一索引能否插入空 5.数据库索引的算法原理 6.乐观锁,悲观锁 7.SQL怎样判断字段是否为空
WEB 1.post和get区别是什么? 2.cookie/session有什么区别? 3.web集群中登录态如何共享? 4.xss、csrf等如何防范? 5.sql注入如何防范? 6.浏览器缓存策略 7.Ajax如何解决跨域问题? 8.什么是ajax,为什么要使用ajax? 9.浅谈你对ajax的认识? 10.Cookie和Session以及Servlet的生命周期? 11.说一下你熟悉的常用linux命令? 12.后台传过的json数据前台怎么接收? 13.后台传过来一个集合前台怎么接收? 14.tomcat的优化方式? 15.http协议有哪些部分组成?
框架 1.Spring的AOP和IOC原理,类内部调用是否会触发AOP的逻辑?为什么? 2.Spring的事务传播属性或数据库的事务隔离级别,实现原理。 3.velocity如何避免引用空属性的报错? 4.Spring的单例和原型怎么配置,还用过其它什么scope? 5.log4j或者logback的日记记录文件在哪里?(来访问题,怎么确定日志打到哪里了)
工具 1.maven中怎么排除包冲突 2.IDEA合并代码怎么做?
分布式 1.如何实现分布式锁 2.分布式Session怎样实现 3.如何消除分布式事务 4.MetaQ,Zookeeper的原理和适用场景 5.Redis3.0的分布式集群架构
高并发 1.原子计数器AtomicInteger的技术实现 2.volatile的内存模型 3.接口服务如何防止重复调用 4.自己使用过的多线程场景是什么?线程安全如何保证?java的并发框架是否用过?线程池是否用过?RejectedExecutionHandler是否知道?
设计 1.对设计模式的了解,在项目中的实际应用。 2.排序算法描述 3.在项目中是否采用分层的结构,是怎样划分的,各层之间采用了哪些相关技术?
Java内存模型和GC 1. 描述下Java的内存模型。区分Young Generation、Tenured Generation、Permanent Generation(Java8以下)。Eden Area、S0、S1 2. 什么是Minor GC?什么是Major GC(Full GC)? 3. 何时会触发Full GC? 4. 常见的垃圾回收算法有哪些。 5. 如何优化JVM性能?JVM的性能取决于其配置是否与应用的功能相匹配。如何设置JVM相关参数?-Xms、-Xmx,-XX:NewRatio、-Xmn(设定初始化和最大新生代大小)、-Xss(设置线程栈大小) 6. 从Efficiency、Throughput、Concurrency、Overhead、JVM version等角度考量,如何选择垃圾收集器的实现? 综合: 1、面向对象的特征有哪些方面? 96、阐述Servlet和CGI的区别? 126、什么是ORM? 171..整数数组按照绝对值排序 172.ConcurrentHashMap的数据结构 173.内存管理机制,如何回收过期对象,判断依据,内存溢出场景,如何排查 174.equals和==的区别 175.final,finally, finalize 176.常用的集合类有哪些?区别是什么?HashMap和HashTable的区别,那些事线程安全的哪些不是? 177.Object中常见的方法 178.java中的异常体系,异常出现的情况如:ClassNotFound、空指针等。 179.Jvm的内存模型是怎么样的?应用突然变得很慢,如何定位? 180.java的异常类层次结构 181.对jdk8新特征有什么了解? 182.java的类加载机制 183.原子类的实现 184.Error,Exception和RuntimeException的区别,作用又是什么? 185.sleep()和wait()区别是什么?
Springboot(含答案) 1.开启 Spring Boot 特性有哪几种方式? 1)继承spring-boot-starter-parent项目 2)导入spring-boot-dependencies项目依赖 2.Spring Boot 需要独立的容器运行吗? 可以不需要,内置了 Tomcat/ Jetty 等容器。 3. 运行 Spring Boot 有哪几种方式? 1)打包用命令或者放到容器中运行 2)用 Maven/ Gradle 插件运行 3)直接执行 main 方法运行 4. Spring Boot 自动配置原理是什么? 注解 @EnableAutoConfiguration, @Configuration, @ConditionalOnClass 就是自动配置的核心,首先它得是一个配置文件,其次根据类路径下是否有这个类去自动配置。 5. Spring Boot 的目录结构是怎样的? cn 这个目录结构是主流及推荐的做法,而在主入口类上加上 @SpringBootApplication 注解来开启 Spring Boot 的各项能力,如自动配置、组件扫描等。
6. 你如何理解 Spring Boot 中的 Starters? Starters可以理解为启动器,它包含了一系列可以集成到应用里面的依赖包,你可以一站式集成 Spring 及其他技术,而不需要到处找示例代码和依赖包。如你想使用 Spring JPA 访问数据库,只要加入 spring-boot-starter-data-jpa 启动器依赖就能使用了。 Starters包含了许多项目中需要用到的依赖,它们能快速持续的运行,都是一系列得到支持的管理传递性依赖。
7. 如何在 Spring Boot 启动的时候运行一些特定的代码? 可以实现接口 ApplicationRunner 或者 CommandLineRunner,这两个接口实现方式一样,它们都只提供了一个 run 方法。
8. Spring Boot 有哪几种读取配置的方式? Spring Boot 可以通过 @PropertySource,@Value,@Environment, @ConfigurationProperties 来绑定变量
9. Spring Boot 支持哪些日志框架?推荐和默认的日志框架是哪个? Spring Boot 支持 Java Util Logging, Log4j2, Lockback 作为日志框架,如果你使用 Starters 启动器,Spring Boot 将使用 Logback 作为默认日志框架
10.SpringBoot 实现热部署有哪几种方式? 主要有两种方式: · Spring Loaded · Spring-boot-devtools Spring-boot-devtools 11.你如何理解 Spring Boot 配置加载顺序? 在 Spring Boot 里面,可以使用以下几种方式来加载配置。 1)properties文件; 2)YAML文件; 3)系统环境变量; 4)命令行参数; 等等…… 12. Spring Boot 如何定义多套不同环境配置? 提供多套配置文件,如: applcation.properties
13. Spring Boot 可以兼容老 Spring 项目吗,如何做? 可以兼容,使用 @ImportResource 注解导入老 Spring 项目配置文件。 14. 保护 Spring Boot 应用有哪些方法? · 在生产中使用HTTPS · 使用Snyk检查你的依赖关系 · 升级到最新版本 · 启用CSRF保护 · 使用内容安全策略防止XSS攻击 · …
15. Spring Boot 2.X 有什么新特性?与 1.X 有什么区别? · 配置变更 · JDK 版本升级 · 第三方类库升级 · 响应式 Spring 编程支持 · HTTP/2 支持 · 配置属性绑定 · 更多改进与加强…
多线程:(含答案)
1、多线程有什么用? 一个可能在很多人看来很扯淡的一个问题:我会用多线程就好了,还管它有什么用?在我看来,这个回答更扯淡。所谓"知其然知其所以然","会用"只是"知其然","为什么用"才是"知其所以然",只有达到"知其然知其所以然"的程度才可以说是把一个知识点运用自如。OK,下面说说对这个问题的看法: 1)发挥多核CPU的优势 随着工业的进步,现在的笔记本、台式机乃至商用的应用服务器至少也都是双核的,4核、8核甚至16核的也都不少见,如果是单线程的程序,那么在双核CPU上就浪费了50%,在4核CPU上就浪费了75%。单核CPU上所谓的"多线程"那是假的多线程,同一时间处理器只会处理一段逻辑,只不过线程之间切换得比较快,看着像多个线程"同时"运行罢了。多核CPU上的多线程才是真正的多线程,它能让你的多段逻辑同时工作,多线程,可以真正发挥出多核CPU的优势来,达到充分利用CPU的目的。 2)防止阻塞 从程序运行效率的角度来看,单核CPU不但不会发挥出多线程的优势,反而会因为在单核CPU上运行多线程导致线程上下文的切换,而降低程序整体的效率。但是单核CPU我们还是要应用多线程,就是为了防止阻塞。试想,如果单核CPU使用单线程,那么只要这个线程阻塞了,比方说远程读取某个数据吧,对端迟迟未返回又没有设置超时时间,那么你的整个程序在数据返回回来之前就停止运行了。多线程可以防止这个问题,多条线程同时运行,哪怕一条线程的代码执行读取数据阻塞,也不会影响其它任务的执行。 3)便于建模 这是另外一个没有这么明显的优点了。假设有一个大的任务A,单线程编程,那么就要考虑很多,建立整个程序模型比较麻烦。但是如果把这个大的任务A分解成几个小任务,任务B、任务C、任务D,分别建立程序模型,并通过多线程分别运行这几个任务,那就简单很多了。
2、创建线程的方式 比较常见的一个问题了,一般就是两种: 1)继承Thread类 2)实现Runnable接口 至于哪个好,不用说肯定是后者好,因为实现接口的方式比继承类的方式更灵活,也能减少程序之间的耦合度,面向接口编程也是设计模式6大原则的核心。 其实还有第3种,点击<>这里了解更多。
3、start()方法和run()方法的区别 只有调用了start()方法,才会表现出多线程的特性,不同线程的run()方法里面的代码交替执行。如果只是调用run()方法,那么代码还是同步执行的,必须等待一个线程的run()方法里面的代码全部执行完毕之后,另外一个线程才可以执行其run()方法里面的代码。
4、Runnable接口和Callable接口的区别 有点深的问题了,也看出一个Java程序员学习知识的广度。 Runnable接口中的run()方法的返回值是void,它做的事情只是纯粹地去执行run()方法中的代码而已;Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。 这其实是很有用的一个特性,因为多线程相比单线程更难、更复杂的一个重要原因就是因为多线程充满着未知性,某条线程是否执行了?某条线程执行了多久?某条线程执行的时候我们期望的数据是否已经赋值完毕?无法得知,我们能做的只是等待这条多线程的任务执行完毕而已。而Callable+Future/FutureTask却可以获取多线程运行的结果,可以在等待时间太长没获取到需要的数据的情况下取消该线程的任务,真的是非常有用。
5、CyclicBarrier和CountDownLatch的区别 两个看上去有点像的类,都在java.util.concurrent下,都可以用来表示代码运行到某个点上,二者的区别在于: 1)CyclicBarrier的某个线程运行到某个点上之后,该线程即停止运行,直到所有的线程都到达了这个点,所有线程才重新运行;CountDownLatch则不是,某线程运行到某个点上之后,只是给某个数值-1而已,该线程继续运行。 2)CyclicBarrier只能唤起一个任务,CountDownLatch可以唤起多个任务。 3) CyclicBarrier可重用,CountDownLatch不可重用,计数值为0该CountDownLatch就不可再用了。
6、volatile关键字的作用 一个非常重要的问题,是每个学习、应用多线程的Java程序员都必须掌握的。理解volatile关键字的作用的前提是要理解Java内存模型,这里就不讲Java内存模型了,可以参见第31点,volatile关键字的作用主要有两个: 1)多线程主要围绕可见性和原子性两个特性而展开,使用volatile关键字修饰的变量,保证了其在多线程之间的可见性,即每次读取到volatile变量,一定是最新的数据。 2)代码底层执行不像我们看到的高级语言----Java程序这么简单,它的执行是Java代码-->字节码-->根据字节码执行对应的C/C++代码-->C/C++代码被编译成汇编语言-->和硬件电路交互,现实中,为了获取更好的性能JVM可能会对指令进行重排序,多线程下可能会出现一些意想不到的问题。使用volatile则会对禁止语义重排序,当然这也一定程度上降低了代码执行效率。 从实践角度而言,volatile的一个重要作用就是和CAS结合,保证了原子性,详细的可以参见java.util.concurrent.atomic包下的类,比如AtomicInteger,更多详情请点击<>这里进行学习。
7、什么是线程安全 又是一个理论的问题,各式各样的答案有很多,我给出一个个人认为解释地最好的:如果你的代码在多线程下执行和在单线程下执行永远都能获得一样的结果,那么你的代码就是线程安全的。 这个问题有值得一提的地方,就是线程安全也是有几个级别的: 1)不可变 像String、Integer、Long这些,都是final类型的类,任何一个线程都改变不了它们的值,要改变除非新创建一个,因此这些不可变对象不需要任何同步手段就可以直接在多线程环境下使用 2)绝对线程安全 不管运行时环境如何,调用者都不需要额外的同步措施。要做到这一点通常需要付出许多额外的代价,Java中标注自己是线程安全的类,实际上绝大多数都不是线程安全的,不过绝对线程安全的类,Java中也有,比方说CopyOnWriteArrayList、CopyOnWriteArraySet 3)相对线程安全 相对线程安全也就是我们通常意义上所说的线程安全,像Vector这种,add、remove方法都是原子操作,不会被打断,但也仅限于此,如果有个线程在遍历某个Vector、有个线程同时在add这个Vector,99%的情况下都会出现ConcurrentModificationException,也就是fail-fast机制。 4)线程非安全 这个就没什么好说的了,ArrayList、LinkedList、HashMap等都是线程非安全的类,点击<>这里了解为什么不安全。
8、Java中如何获取到线程dump文件 死循环、死锁、阻塞、页面打开慢等问题,打线程dump是最好的解决问题的途径。所谓线程dump也就是线程堆栈,获取到线程堆栈有两步: 1)获取到线程的pid,可以通过使用jps命令,在Linux环境下还可以使用ps -ef | grep java 2)打印线程堆栈,可以通过使用jstack pid命令,在Linux环境下还可以使用kill -3 pid 另外提一点,Thread类提供了一个getStackTrace()方法也可以用于获取线程堆栈。这是一个实例方法,因此此方法是和具体线程实例绑定的,每次获取获取到的是具体某个线程当前运行的堆栈。
9、一个线程如果出现了运行时异常会怎么样 如果这个异常没有被捕获的话,这个线程就停止执行了。另外重要的一点是:如果这个线程持有某个某个对象的监视器,那么这个对象监视器会被立即释放
10、如何在两个线程之间共享数据 通过在线程之间共享对象就可以了,然后通过wait/notify/notifyAll、await/signal/signalAll进行唤起和等待,比方说阻塞队列BlockingQueue就是为线程之间共享数据而设计的
11、sleep方法和wait方法有什么区别 这个问题常问,sleep方法和wait方法都可以用来放弃CPU一定的时间,不同点在于如果线程持有某个对象的监视器,sleep方法不会放弃这个对象的监视器,wait方法会放弃这个对象的监视器
12、生产者消费者模型的作用是什么 这个问题很理论,但是很重要: 1)通过平衡生产者的生产能力和消费者的消费能力来提升整个系统的运行效率,这是生产者消费者模型最重要的作用 2)解耦,这是生产者消费者模型附带的作用,解耦意味着生产者和消费者之间的联系少,联系越少越可以独自发展而不需要收到相互的制约
13、ThreadLocal有什么用 简单说ThreadLocal就是一种以空间换时间的做法,在每个Thread里面维护了一个以开地址法实现的ThreadLocal.ThreadLocalMap,把数据进行隔离,数据不共享,自然就没有线程安全方面的问题了
14、为什么wait()方法和notify()/notifyAll()方法要在同步块中被调用 这是JDK强制的,wait()方法和notify()/notifyAll()方法在调用前都必须先获得对象的锁
15、wait()方法和notify()/notifyAll()方法在放弃对象监视器时有什么区别 wait()方法和notify()/notifyAll()方法在放弃对象监视器的时候的区别在于:wait()方法立即释放对象监视器,notify()/notifyAll()方法则会等待线程剩余代码执行完毕才会放弃对象监视器。
16、为什么要使用线程池 避免频繁地创建和销毁线程,达到线程对象的重用。另外,使用线程池还可以根据项目灵活地控制并发的数目。点击<>这里学习线程池详解。
17、怎么检测一个线程是否持有对象监视器 我也是在网上看到一道多线程面试题才知道有方法可以判断某个线程是否持有对象监视器:Thread类提供了一个holdsLock(Object obj)方法,当且仅当对象obj的监视器被某条线程持有的时候才会返回true,注意这是一个static方法,这意味着"某条线程"指的是当前线程。
18、synchronized和ReentrantLock的区别 synchronized是和if、else、for、while一样的关键字,ReentrantLock是类,这是二者的本质区别。既然ReentrantLock是类,那么它就提供了比synchronized更多更灵活的特性,可以被继承、可以有方法、可以有各种各样的类变量,ReentrantLock比synchronized的扩展性体现在几点上: (1)ReentrantLock可以对获取锁的等待时间进行设置,这样就避免了死锁 (2)ReentrantLock可以获取各种锁的信息 (3)ReentrantLock可以灵活地实现多路通知 另外,二者的锁机制其实也是不一样的。ReentrantLock底层调用的是Unsafe的park方法加锁,synchronized操作的应该是对象头中mark word,这点我不能确定。
19、ConcurrentHashMap的并发度是什么 ConcurrentHashMap的并发度就是segment的大小,默认为16,这意味着最多同时可以有16条线程操作ConcurrentHashMap,这也是ConcurrentHashMap对Hashtable的最大优势,任何情况下,Hashtable能同时有两条线程获取Hashtable中的数据吗?
20、ReadWriteLock是什么 首先明确一下,不是说ReentrantLock不好,只是ReentrantLock某些时候有局限。如果使用ReentrantLock,可能本身是为了防止线程A在写数据、线程B在读数据造成的数据不一致,但这样,如果线程C在读数据、线程D也在读数据,读数据是不会改变数据的,没有必要加锁,但是还是加锁了,降低了程序的性能。 因为这个,才诞生了读写锁ReadWriteLock。ReadWriteLock是一个读写锁接口,ReentrantReadWriteLock是ReadWriteLock接口的一个具体实现,实现了读写的分离,读锁是共享的,写锁是独占的,读和读之间不会互斥,读和写、写和读、写和写之间才会互斥,提升了读写的性能。
21、FutureTask是什么 这个其实前面有提到过,FutureTask表示一个异步运算的任务。FutureTask里面可以传入一个Callable的具体实现类,可以对这个异步运算的任务的结果进行等待获取、判断是否已经完成、取消任务等操作。当然,由于FutureTask也是Runnable接口的实现类,所以FutureTask也可以放入线程池中。
22、Linux环境下如何查找哪个线程使用CPU最长 这是一个比较偏实践的问题,这种问题我觉得挺有意义的。可以这么做: (1)获取项目的pid,jps或者ps -ef | grep java,这个前面有讲过 (2)top -H -p pid,顺序不能改变 这样就可以打印出当前的项目,每条线程占用CPU时间的百分比。注意这里打出的是LWP,也就是操作系统原生线程的线程号,我笔记本山没有部署Linux环境下的Java工程,因此没有办法截图演示,网友朋友们如果公司是使用Linux环境部署项目的话,可以尝试一下。 使用"top -H -p pid"+"jps pid"可以很容易地找到某条占用CPU高的线程的线程堆栈,从而定位占用CPU高的原因,一般是因为不当的代码操作导致了死循环。 最后提一点,"top -H -p pid"打出来的LWP是十进制的,"jps pid"打出来的本地线程号是十六进制的,转换一下,就能定位到占用CPU高的线程的当前线程堆栈了。
23、Java编程写一个会导致死锁的程序 第一次看到这个题目,觉得这是一个非常好的问题。很多人都知道死锁是怎么一回事儿:线程A和线程B相互等待对方持有的锁导致程序无限死循环下去。当然也仅限于此了,问一下怎么写一个死锁的程序就不知道了,这种情况说白了就是不懂什么是死锁,懂一个理论就完事儿了,实践中碰到死锁的问题基本上是看不出来的。 真正理解什么是死锁,这个问题其实不难,几个步骤: 1)两个线程里面分别持有两个Object对象:lock1和lock2。这两个lock作为同步代码块的锁; 2)线程1的run()方法中同步代码块先获取lock1的对象锁,Thread.sleep(xxx),时间不需要太多,50毫秒差不多了,然后接着获取lock2的对象锁。这么做主要是为了防止线程1启动一下子就连续获得了lock1和lock2两个对象的对象锁 3)线程2的run)(方法中同步代码块先获取lock2的对象锁,接着获取lock1的对象锁,当然这时lock1的对象锁已经被线程1锁持有,线程2肯定是要等待线程1释放lock1的对象锁的 这样,线程1"睡觉"睡完,线程2已经获取了lock2的对象锁了,线程1此时尝试获取lock2的对象锁,便被阻塞,此时一个死锁就形成了。代码就不写了,占的篇幅有点多,Java多线程7:死锁这篇文章里面有,就是上面步骤的代码实现。 点击<>这里提供了一个死锁的案例。
24、怎么唤醒一个阻塞的线程 如果线程是因为调用了wait()、sleep()或者join()方法而导致的阻塞,可以中断线程,并且通过抛出InterruptedException来唤醒它;如果线程遇到了IO阻塞,无能为力,因为IO是操作系统实现的,Java代码并没有办法直接接触到操作系统。
25、不可变对象对多线程有什么帮助 前面有提到过的一个问题,不可变对象保证了对象的内存可见性,对不可变对象的读取不需要进行额外的同步手段,提升了代码执行效率。
26、什么是多线程的上下文切换 多线程的上下文切换是指CPU控制权由一个已经正在运行的线程切换到另外一个就绪并等待获取CPU执行权的线程的过程。
27、如果你提交任务时,线程池队列已满,这时会发生什么 这里区分一下: 1)如果使用的是无界队列LinkedBlockingQueue,也就是无界队列的话,没关系,继续添加任务到阻塞队列中等待执行,因为LinkedBlockingQueue可以近乎认为是一个无穷大的队列,可以无限存放任务 2)如果使用的是有界队列比如ArrayBlockingQueue,任务首先会被添加到ArrayBlockingQueue中,ArrayBlockingQueue满了,会根据maximumPoolSize的值增加线程数量,如果增加了线程数量还是处理不过来,ArrayBlockingQueue继续满,那么则会使用拒绝策略RejectedExecutionHandler处理满了的任务,默认是AbortPolicy
28、Java中用到的线程调度算法是什么 抢占式。一个线程用完CPU之后,操作系统会根据线程优先级、线程饥饿情况等数据算出一个总的优先级并分配下一个时间片给某个线程执行。
29、Thread.sleep(0)的作用是什么 这个问题和上面那个问题是相关的,我就连在一起了。由于Java采用抢占式的线程调度算法,因此可能会出现某条线程常常获取到CPU控制权的情况,为了让某些优先级比较低的线程也能获取到CPU控制权,可以使用Thread.sleep(0)手动触发一次操作系统分配时间片的操作,这也是平衡CPU控制权的一种操作。
30、什么是自旋 很多synchronized里面的代码只是一些很简单的代码,执行时间非常快,此时等待的线程都加锁可能是一种不太值得的操作,因为线程阻塞涉及到用户态和内核态切换的问题。既然synchronized里面的代码执行得非常快,不妨让等待锁的线程不要被阻塞,而是在synchronized的边界做忙循环,这就是自旋。如果做了多次忙循环发现还没有获得锁,再阻塞,这样可能是一种更好的策略。
31、什么是Java内存模型 Java内存模型定义了一种多线程访问Java内存的规范。Java内存模型要完整讲不是这里几句话能说清楚的,我简单总结一下Java内存模型的几部分内容: 1)Java内存模型将内存分为了主内存和工作内存。类的状态,也就是类之间共享的变量,是存储在主内存中的,每次Java线程用到这些主内存中的变量的时候,会读一次主内存中的变量,并让这些内存在自己的工作内存中有一份拷贝,运行自己线程代码的时候,用到这些变量,操作的都是自己工作内存中的那一份。在线程代码执行完毕之后,会将最新的值更新到主内存中去 2)定义了几个原子操作,用于操作主内存和工作内存中的变量 3)定义了volatile变量的使用规则 4)happens-before,即先行发生原则,定义了操作A必然先行发生于操作B的一些规则,比如在同一个线程内控制流前面的代码一定先行发生于控制流后面的代码、一个释放锁unlock的动作一定先行发生于后面对于同一个锁进行锁定lock的动作等等,只要符合这些规则,则不需要额外做同步措施,如果某段代码不符合所有的happens-before规则,则这段代码一定是线程非安全的
32、什么是CAS
33、什么是乐观锁和悲观锁 1)乐观锁:就像它的名字一样,对于并发间操作产生的线程安全问题持乐观状态,乐观锁认为竞争不总是会发生,因此它不需要持有锁,将比较-替换这两个动作作为一个原子操作尝试去修改内存中的变量,如果失败则表示发生冲突,那么就应该有相应的重试逻辑。 2)悲观锁:还是像它的名字一样,对于并发间操作产生的线程安全问题持悲观状态,悲观锁认为竞争总是会发生,因此每次对某资源进行操作时,都会持有一个独占的锁,就像synchronized,不管三七二十一,直接上了锁就操作资源了。 点击<>这里了解更多乐观锁与悲观锁详情。
34、什么是AQS 简单说一下AQS,AQS全称为AbstractQueuedSychronizer,翻译过来应该是抽象队列同步器。 如果说java.util.concurrent的基础是CAS的话,那么AQS就是整个Java并发包的核心了,ReentrantLock、CountDownLatch、Semaphore等等都用到了它。AQS实际上以双向队列的形式连接所有的Entry,比方说ReentrantLock,所有等待的线程都被放在一个Entry中并连成双向队列,前面一个线程使用ReentrantLock好了,则双向队列实际上的第一个Entry开始运行。 AQS定义了对双向队列所有的操作,而只开放了tryLock和tryRelease方法给开发者使用,开发者可以根据自己的实现重写tryLock和tryRelease方法,以实现自己的并发功能。
35、单例模式的线程安全性 老生常谈的问题了,首先要说的是单例模式的线程安全意味着:某个类的实例在多线程环境下只会被创建一次出来。单例模式有很多种的写法,我总结一下: 1)饿汉式单例模式的写法:线程安全 2)懒汉式单例模式的写法:非线程安全 3)双检锁单例模式的写法:线程安全
36、Semaphore有什么作用 Semaphore就是一个信号量,它的作用是限制某段代码块的并发数。Semaphore有一个构造函数,可以传入一个int型整数n,表示某段代码最多只有n个线程可以访问,如果超出了n,那么请等待,等到某个线程执行完毕这段代码块,下一个线程再进入。由此可以看出如果Semaphore构造函数中传入的int型整数n=1,相当于变成了一个synchronized了。
37、Hashtable的size()方法中明明只有一条语句"return count",为什么还要做同步? 这是我之前的一个困惑,不知道大家有没有想过这个问题。某个方法中如果有多条语句,并且都在操作同一个类变量,那么在多线程环境下不加锁,势必会引发线程安全问题,这很好理解,但是size()方法明明只有一条语句,为什么还要加锁? 关于这个问题,在慢慢地工作、学习中,有了理解,主要原因有两点: 1)同一时间只能有一条线程执行固定类的同步方法,但是对于类的非同步方法,可以多条线程同时访问。所以,这样就有问题了,可能线程A在执行Hashtable的put方法添加数据,线程B则可以正常调用size()方法读取Hashtable中当前元素的个数,那读取到的值可能不是最新的,可能线程A添加了完了数据,但是没有对size++,线程B就已经读取size了,那么对于线程B来说读取到的size一定是不准确的。而给size()方法加了同步之后,意味着线程B调用size()方法只有在线程A调用put方法完毕之后才可以调用,这样就保证了线程安全性 2)CPU执行代码,执行的不是Java代码,这点很关键,一定得记住。Java代码最终是被翻译成机器码执行的,机器码才是真正可以和硬件电路交互的代码。即使你看到Java代码只有一行,甚至你看到Java代码编译之后生成的字节码也只有一行,也不意味着对于底层来说这句语句的操作只有一个。一句"return count"假设被翻译成了三句汇编语句执行,一句汇编语句和其机器码做对应,完全可能执行完第一句,线程就切换了。
38、线程类的构造方法、静态块是被哪个线程调用的 这是一个非常刁钻和狡猾的问题。请记住:线程类的构造方法、静态块是被new这个线程类所在的线程所调用的,而run方法里面的代码才是被线程自身所调用的。 如果说上面的说法让你感到困惑,那么我举个例子,假设Thread2中new了Thread1,main函数中new了Thread2,那么: 1)Thread2的构造方法、静态块是main线程调用的,Thread2的run()方法是Thread2自己调用的 2)Thread1的构造方法、静态块是Thread2调用的,Thread1的run()方法是Thread1自己调用的
39、同步方法和同步块,哪个是更好的选择 同步块,这意味着同步块之外的代码是异步执行的,这比同步整个方法更提升代码的效率。请知道一条原则:同步的范围越小越好。 借着这一条,我额外提一点,虽说同步的范围越少越好,但是在Java虚拟机中还是存在着一种叫做锁粗化的优化方法,这种方法就是把同步范围变大。这是有用的,比方说StringBuffer,它是一个线程安全的类,自然最常用的append()方法是一个同步方法,我们写代码的时候会反复append字符串,这意味着要进行反复的加锁->解锁,这对性能不利,因为这意味着Java虚拟机在这条线程上要反复地在内核态和用户态之间进行切换,因此Java虚拟机会将多次append方法调用的代码进行一个锁粗化的操作,将多次的append的操作扩展到append方法的头尾,变成一个大的同步块,这样就减少了加锁-->解锁的次数,有效地提升了代码执行的效率。
40、高并发、任务执行时间短的业务怎样使用线程池?并发不高、任务执行时间长的业务怎样使用线程池?并发高、业务执行时间长的业务怎样使用线程池? 这是我在并发编程网上看到的一个问题,把这个问题放在最后一个,希望每个人都能看到并且思考一下,因为这个问题非常好、非常实际、非常专业。关于这个问题,个人看法是: 1)高并发、任务执行时间短的业务,线程池线程数可以设置为CPU核数+1,减少线程上下文的切换 2)并发不高、任务执行时间长的业务要区分开看: a)假如是业务时间长集中在IO操作上,也就是IO密集型的任务,因为IO操作并不占用CPU,所以不要让所有的CPU闲下来,可以加大线程池中的线程数目,让CPU处理更多的业务 b)假如是业务时间长集中在计算操作上,也就是计算密集型任务,这个就没办法了,和(1)一样吧,线程池中的线程数设置得少一些,减少线程上下文的切换 c)并发高、业务执行时间长,解决这种类型任务的关键不在于线程池而在于整体架构的设计,看看这些业务里面某些数据是否能做缓存是第一步,增加服务器是第二步,至于线程池的设置,设置参考其他有关线程池的文章。最后,业务执行时间长的问题,也可能需要分析一下,看看能不能使用中间件对任务进行拆分和解耦。
DUBBO
1、Dubbo是什么? Dubbo是阿里巴巴开源的基于 Java 的高性能 RPC 分布式服务框架,现已成为 Apache 基金会孵化项目。 面试官问你如果这个都不清楚,那下面的就没必要问了。
2、为什么要用Dubbo? 因为是阿里开源项目,国内很多互联网公司都在用,已经经过很多线上考验。内部使用了 Netty、Zookeeper,保证了高性能高可用性。 使用 Dubbo 可以将核心业务抽取出来,作为独立的服务,逐渐形成稳定的服务中心,可用于提高业务复用灵活扩展,使前端应用能更快速的响应多变的市场需求。 下面这张图可以很清楚的诠释,最重要的一点是,分布式架构可以承受更大规模的并发流量。
下面是 Dubbo 的服务治理图。
3、Dubbo 和 Spring Cloud 有什么区别? 两个没关联,如果硬要说区别,有以下几点。 1)通信方式不同 Dubbo 使用的是 RPC 通信,而 Spring Cloud 使用的是 HTTP RESTFul 方式。 2)组成部分不同
4、dubbo都支持什么协议,推荐用哪种? · dubbo://(推荐) · rmi:// · hessian:// · http:// · webservice:// · thrift:// · memcached:// · redis:// · rest://
5、Dubbo需要 Web 容器吗? 不需要,如果硬要用 Web 容器,只会增加复杂性,也浪费资源。 6、Dubbo内置了哪几种服务容器? o Spring Container o Jetty Container o Log4j Container o Dubbo 的服务容器只是一个简单的 Main 方法,并加载一个简单的 Spring 容器,用于暴露服务。
7、Dubbo里面有哪几种节点角色?
8、画一画服务注册与发现的流程图
该图来自 Dubbo 官网,供你参考,如果你说你熟悉 Dubbo, 面试官经常会让你画这个图,记好了。 9、Dubbo默认使用什么注册中心,还有别的选择吗? 推荐使用 Zookeeper 作为注册中心,还有 Redis、Multicast、Simple 注册中心,但不推荐。 10、Dubbo有哪几种配置方式? 1)Spring 配置方式 11、Dubbo 核心的配置有哪些? 我曾经面试就遇到过面试官让你写这些配置,我也是蒙逼。
配置之间的关系见下图。
12、在 Provider 上可以配置的 Consumer 端的属性有哪些? 1)timeout:方法调用超时
13、Dubbo启动时如果依赖的服务不可用会怎样? Dubbo 缺省会在启动时检查依赖的服务是否可用,不可用时会抛出异常,阻止 Spring 初始化完成,默认 check="true",可以通过 check="false" 关闭检查。
14、Dubbo推荐使用什么序列化框架,你知道的还有哪些? 推荐使用Hessian序列化,还有Duddo、FastJson、Java自带序列化。
15、Dubbo默认使用的是什么通信框架,还有别的选择吗? Dubbo 默认使用 Netty 框架,也是推荐的选择,另外内容还集成有Mina、Grizzly。 16、Dubbo有哪几种集群容错方案,默认是哪种?
17、Dubbo有哪几种负载均衡策略,默认是哪种?
18、注册了多个同一样的服务,如果测试指定的某一个服务呢? 可以配置环境点对点直连,绕过注册中心,将以服务接口为单位,忽略注册中心的提供者列表。 19、Dubbo支持服务多协议吗? Dubbo 允许配置多协议,在不同服务上支持不同协议或者同一服务上同时支持多种协议。 20、当一个服务接口有多种实现时怎么做? 当一个接口有多种实现时,可以用 group 属性来分组,服务提供方和消费方都指定同一个 group 即可。 21、服务上线怎么兼容旧版本? 可以用版本号(version)过渡,多个不同版本的服务注册到注册中心,版本号不同的服务相互间不引用。这个和服务分组的概念有一点类似。 22、Dubbo可以对结果进行缓存吗? 可以,Dubbo 提供了声明式缓存,用于加速热门数据的访问速度,以减少用户加缓存的工作量。 23、Dubbo服务之间的调用是阻塞的吗? 默认是同步等待结果阻塞的,支持异步调用。 Dubbo 是基于 NIO 的非阻塞实现并行调用,客户端不需要启动多线程即可完成并行调用多个远程服务,相对多线程开销较小,异步调用会返回一个 Future 对象。 异步调用流程图如下。
24、Dubbo支持分布式事务吗? 目前暂时不支持,后续可能采用基于 JTA/XA 规范实现,如以图所示。
25、Dubbo telnet 命令能做什么? dubbo 通过 telnet 命令来进行服务治理 telnet localhost 8090 26、Dubbo支持服务降级吗? Dubbo 2.2.0 以上版本支持。 27、Dubbo如何优雅停机? Dubbo 是通过 JDK 的 ShutdownHook 来完成优雅停机的,所以如果使用 kill -9 PID 等强制关闭指令,是不会执行优雅停机的,只有通过 kill PID 时,才会执行。 28、服务提供者能实现失效踢出是什么原理? 服务失效踢出基于 Zookeeper 的临时节点原理。 29、如何解决服务调用链过长的问题? Dubbo 可以使用 Pinpoint 和 Apache Skywalking(Incubator) 实现分布式服务追踪,当然还有其他很多方案。 30、服务读写推荐的容错策略是怎样的? 读操作建议使用 Failover 失败自动切换,默认重试两次其他服务器。 写操作建议使用 Failfast 快速失败,发一次调用失败就立即报错。 31、Dubbo必须依赖的包有哪些? Dubbo 必须依赖 JDK,其他为可选。 32、Dubbo的管理控制台能做什么? 管理控制台主要包含:路由规则,动态配置,服务降级,访问控制,权重调整,负载均衡,等管理功能。 33、说说 Dubbo 服务暴露的过程。 Dubbo 会在 Spring 实例化完 bean 之后,在刷新容器最后一步发布 ContextRefreshEvent 事件的时候,通知实现了 ApplicationListener 的 ServiceBean 类进行回调 onApplicationEvent 事件方法,Dubbo 会在这个方法中调用 ServiceBean 父类 ServiceConfig 的 export 方法,而该方法真正实现了服务的(异步或者非异步)发布。 34、Dubbo 停止维护了吗? 2014 年开始停止维护过几年,17 年开始重新维护,并进入了 Apache 项目。 35、Dubbo 和 Dubbox 有什么区别? Dubbox 是继 Dubbo 停止维护后,当当网基于 Dubbo 做的一个扩展项目,如加了服务可 Restful 调用,更新了开源组件等。 36、你还了解别的分布式框架吗? 别的还有 Spring cloud、Facebook 的 Thrift、Twitter 的 Finagle 等。 37、Dubbo 能集成 Spring Boot 吗? 可以的,项目地址如下。 https://github.com/apache/incubator-dubbo-spring-boot-project 38、在使用过程中都遇到了些什么问题? Dubbo 的设计目的是为了满足高并发小数据量的 rpc 调用,在大数据量下的性能表现并不好,建议使用 rmi 或 http 协议。 39、你读过 Dubbo 的源码吗? 要了解 Dubbo 就必须看其源码,了解其原理,花点时间看下吧,网上也有很多教程。 40、你觉得用 Dubbo 好还是 Spring Cloud 好? 扩展性的问题,没有好坏,只有适合不适合,不过我好像更倾向于使用 Dubbo, Spring Cloud 版本升级太快,组件更新替换太频繁,配置太繁琐,还有很多我觉得是没有 Dubbo 顺手的地方…… Redis(含答案): 1、什么是Redis? Redis本质上是一个Key-Value类型的内存数据库,很像memcached,整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flush到硬盘上进行保存。因为是纯内存操作,Redis的性能非常出色,每秒可以处理超过 10万次读写操作,是已知性能最快的Key-Value DB。 Redis的出色之处不仅仅是性能,Redis最大的魅力是支持保存多种数据结构,此外单个value的最大限制是1GB,不像 memcached只能保存1MB的数据,因此Redis可以用来实现很多有用的功能,比方说用他的List来做FIFO双向链表,实现一个轻量级的高性 能消息队列服务,用他的Set可以做高性能的tag系统等等。另外Redis也可以对存入的Key-Value设置expire时间,因此也可以被当作一 个功能加强版的memcached来用。 Redis的主要缺点是数据库容量受到物理内存的限制,不能用作海量数据的高性能读写,因此Redis适合的场景主要局限在较小数据量的高性能操作和运算上。
2、Redis相比memcached有哪些优势? (1) memcached所有的值均是简单的字符串,redis作为其替代者,支持更为丰富的数据类型 (2) redis的速度比memcached快很多 (3) redis可以持久化其数据 3、Redis支持哪几种数据类型? String、List、Set、Sorted Set、hashes 4、Redis主要消耗什么物理资源? 内存。
5、Redis的全称是什么? Remote Dictionary Server。
6、Redis有哪几种数据淘汰策略? noeviction:返回错误当内存限制达到并且客户端尝试执行会让更多内存被使用的命令(大部分的写入指令,但DEL和几个例外) allkeys-lru: 尝试回收最少使用的键(LRU),使得新添加的数据有空间存放。 volatile-lru: 尝试回收最少使用的键(LRU),但仅限于在过期集合的键,使得新添加的数据有空间存放。 allkeys-random: 回收随机的键使得新添加的数据有空间存放。 volatile-random: 回收随机的键使得新添加的数据有空间存放,但仅限于在过期集合的键。 volatile-ttl: 回收在过期集合的键,并且优先回收存活时间(TTL)较短的键,使得新添加的数据有空
|
|||||
| >> 相关文章 | |||||