Java并发编程经典题目

本文尚未完成,请耐心等待。

以下问题是网络搜集,答案则是我个人回答,由于本人水平非常有限,如有错误,恳请指出。


第一部分:理论篇


多线程


Q:java中有几种方法可以创建一个线程?

how many ways to create a thread in java

Q:如何停止一个正在运行的线程?

how to stop a java thread

Q:notify()和notifyAll()有什么区别?

notify()是唤醒一个线程。只唤醒一个。

notifyAll()是唤醒全部线程,但是注意是一个个唤醒,唤醒一个再唤下一个。

随机唤醒和唤醒全部。

Q:sleep()和 wait()有什么区别?

定时等待和在监视器上等待,不同范畴。

Q:什么是Daemon线程?它有什么意义?

守护线程,不需要上层逻辑介入的后台线程 ,比如GC。

Q:java如何实现多线程之间的通讯和协作?

中断和 直接或间接访问对方实例。

线程同步(使用了synchronized)和线程通讯(使用了wait,notify)


Q:什么是可重入锁(ReentrantLock)?

更高级的锁,附加更多特性。

Q:当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?

可进入非synchronized方法。

Q:synchronized和java.util.concurrent.locks.Lock的异同?

后者具有更丰富的特性。

ReentrantLock 相对于固有锁synchronized,同样是可重入的,在某些vm版本上提供了比固有锁更高的性能,提供了更丰富的锁特性,比如可中断的锁,可等待的锁,平等锁以及非块结构的加锁。从代码上尽量用固有锁,vm会对固有锁做一定的优化,并且代码可维护和稳定。只有在需要ReentrantLock的一些特性时,可以考虑用ReentrantLock实现。

ReentrantLock 和synchronized比较:
性能上:
synchronized在Java5.0性能大胜synchronized,Java 6中synchronized 略有胜出—-摘自《java并发编程实战》(13.4)
当时我说synchronized在今后的JVM(JIT)优化的可能性比ReentrantLock大,回来后我翻翻资料,发现在ReentrantLock 和synchronized还真有一些需要了解的知识,所以特别整理下发给大家。
1.为什么JUC框架出现LOCK?
ReentrantLock并不是替代synchronized的方法,而是当内置锁不适用时,作为一种可选的高级功能。
2.那么Synchronized有哪些缺点?
①. 只有一个condition与锁相关联,这个condition是什么?就是synchronized对针对的对象锁。
②. synchronized无法中断一个正在等待获得锁的线程,也即多线程竞争一个锁时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。这种情况对于大量的竞争线程会造成性能的下降等后果。
可见ReentrantLock 是对synchronized补充。
3.我们面对ReentrantLock和synchronized改如何选择?
Synchronized相比Lock,为许多开发人员所熟悉,并且简洁紧凑,如果现有程序已经使用了内置锁,那么尽量保持代码风格统一,尽量不引入Lock,避免两种机制混用,容易令人困惑,也容易发生错误。
在Synchronized无法满足需求的情况下,Lock可以作为一种高级工具,这些功能包括“可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁”否则还是优先使用Synchronized。
最后,未来更可能提升Synchronized而不是Lock的性能,因为Synchronized是JVM的内置属性,他能执行一些优化,例如对线程封闭的锁对象的锁消除优化,通过增加锁的粒度来消除内置锁的同步,而如果基于类库的锁来实现这些功能,则可能性不大。—以上建议摘自《java并发编程实战》(13.4)

Q:乐观锁和悲观锁的理解及如何实现,有哪些实现方式?

乐观锁是假设我已经拿到锁,悲观所是我必须拿到锁,前者用CAS,后者用mutex。

并发框架


Q:SynchronizedMap和ConcurrentHashMap有什么区别?

后者具有更高的并发。

Q:CopyOnWriteArrayList可以用于什么应用场景?

多读少写。

线程安全


Q:什么叫线程安全?servlet是线程安全吗?

在多线程调用情况下,依然表现正常。

Q:同步有几种实现方法?

锁和volatile。

Q:volatile有什么用?能否用一句话说明下volatile的应用场景?

保持可见性,在1写N读的情况下比较适合。

Q:请说明下java的内存模型及其工作流程。

JVM的内存模型可简单理解为,有一块整个JVM共享的主内存,每个线程有自己的变量复本,线程持有的复本与主内存之间使用内部的数据协议,再具体可参考一些blog和周志明的深入理解JVM12章

Q:为什么代码会重排序?

编译器旨在提升性能。

并发容器和框架


Q:如何让一段程序并发的执行,并最终汇总结果?

  1. 使用CyclicBarrier 和CountDownLatch都可以
    使用CyclicBarrier 在多个关口处将多个线程执行结果汇总
    CountDownLatch 在各线程执行完毕后向总线程汇报结果

1 可以用join,或者条件变量

1.CyclicBarrier CountDownLatch join

a.切分问题。
b.每个线程执行的结果存入一个容器。
c.汇总结果,做进一步处理。

看看FutureTask
Future这个只是一个线程池吧,特殊点是它可以获取到任务的一个返回结果。

Fork/Join值得关注

Q:如何合理的配置java线程池?如CPU密集型的任务,基本线程池应该配置多大?IO密集型的任务,基本线程池应该配置多大?用有界队列好还是无界队列好?任务非常多的时候,使用什么阻塞队列能获取最好的吞吐量?

  1. 配置线程池时CPU密集型任务可以少配置线程数,大概和机器的cpu核数相当,可以使得每个线程都在执行任务
    IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数
    有界队列和无界队列的配置需区分业务场景,一般情况下配置有界队列,在一些可能会有爆发性增长的情况下使用无界队列。
    任务非常多时,使用非

2 CPU密集型的CPU个数+1,IO密集型的需要调优,看特定环境,处理快的可以是无界,linkedBlockingQueue吧
阻塞队列使用cas操作替代锁可以获得好的吞吐量。

  1. 配置线程池时CPU密集型任务可以少配置线程数,大概和机器的cpu核数相当,可以使得每个线程都在执行任务
    IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数 有界队列好,无界队列不可控,有可能导致内存不够 synchronousQueue吞吐率最高

Q:如何使用阻塞队列实现一个生产者和消费者模型?请写代码。

3 生产者负责notify,消费者需要wait,条件是有无元素

3 BlockingQueue put take

Q:多读少写的场景应该使用哪个并发容器,为什么使用它?比如你做了一个搜索引擎,搜索引擎每次搜索前需要判断搜索关键词是否在黑名单里,黑名单每天更新一次。

  1. CopyOnWriteArrayList这个容器适用于多读少写…读写并不是在同一个对象上。在写时会大面积复制数组,所以写的性能差,在写完成后将读的引用改为执行写的对象

4 Copyonwrite或者CAS型
4CopyOnWriteArrayList这个容器适用于多读少写 读的时候可以不用加锁

Java中的锁


Q:如何实现乐观锁(CAS)?如何避免ABA问题?

  1. 读取内存值
    比较内存值和期望值
    替换内存值为要替换值

带参数版本来避免aba问题,在读取和替换的时候进行判定版本是否一致

1 利用CPU的cas命令,避免ABA需要使用类似于version,AtomicStampedReference也可以

1 compareAndSet AtomicStampedReference

Q:读写锁可以用于什么应用场景?

  1. 多读少写,读写锁支持多个读操作并发执行,写操作只能由一个线程来操作

2 读多写少
2多读少写

Q:什么时候应该使用可重入锁?

  1. 重入锁指的是在某一个线程中可以多次获得同一把锁,在线程中多次操作有锁的方法。

3 需要使用除了内置锁以外的锁特性,比如可中断,可等待的锁,平等锁等

3可轮询,可中断,定时,非块,公平队列等高级特性时候使用可重入锁

Q:什么场景下可以使用volatile替换synchronized?

  1. 只需要保证共享资源的可见性的时候可以使用volatile替代,synchronized保证可操作的原子性一致性和可见性。
    volatile适用于新值不依赖于就值的情形。

4 1写N读

4 单线程修改变量或不依赖当前值,且不与其他变量构成不变性条件时候使用volatile

并发工具


Q:如何实现一个流控程序,用于控制请求的调用次数?

1 fixed的线程池


第二部分:实战篇


Java多线程面试问题集锦

几个创建线程的写法:

  • 继承Thread类
  • 实现Runnable接口
  • 使用Executor framework (这会创建一个线程池)

问题:列举几种不同的创建线程的方法.
问题:推荐通过哪种方式创建线程,为什么?
问题:简要的说明一下高级线程状态.
问题:yield和sleeping有何区别,sleep()和wait()有何区别?
问题:为什么为了线程安全而锁定一个方法或者一个代码块称为“同步”而不是“锁定”或者“被锁定”
问题:线程如何进行的同步处理?你可以列举出那些同步级别?同步方法和代码块如何区别?

Java多线程面试
java多线程面试题
http://mouselearnjava.iteye.com/blog/1956682
http://my.oschina.net/u/176507/blog/137880
http://yueyemaitian.iteye.com/blog/1387901

http://tutorials.jenkov.com/java-concurrency/blocking-queues.html

参考文献:
Java并发面试题(一)基础
Java并发面试题(二)实战

Java线程池的分析和使用
Java线程池(第二题)
原子操作的实现原理 (锁 第一题)
Java中的读写锁(锁 第二题)
如何设计客户端流控程序 (并发工具 第一题)

评论