目录
多线程
- java中有几种方法可以实现一个线程?
继承Thead 和 实现Runnable, 其实Runnable也是用Thead对象这run里调用 Runnable的方法
- 2.如何停止一个正在运行的线程?
使用Thead的中断和设置共享变量,两者都需被停止的线程自己主动检查。
可以使用正在运行的线程,支持线程中断,那么可以使用线程中断的方法停止这个线程;通常是定义一个volatile的状态变量,在运行线程线程中读这个变量,其它线程中修改这个变量
- notify()和notifyAll()有什么区别?
notify是随机唤醒一个等待某个资源的线程,进入就绪队列等待CPU的调度,notifyAll是唤醒所有的,进入就绪队列等待CPU调度
- sleep()和 wait()有什么区别?
sleep()是当前正在运行的线程主动放弃CPU,进入睡眠状态,如果此时线程已经持有一个锁,则释放这个锁
wait()是等待其它其它线程对某个共享资源的锁释放,等待期间,仍然持有它已经获得的同步锁,这个就是典型的两个线程互相等待对方持有的资源以致死锁的原因
sleep不会放开锁 ,wait会放开。调用wait必须先获取锁,也就是要写在synchronized里面
- 什么是Daemon线程?它有什么意义?
守护线程是后台线程,它通常属于低优先级的线程,守护用户线程,通常做一些垃圾清理的工作,比如GC。当所有的用户线程退出后,守护线程不会立即退出,还出执行一段时间,具体多少时间,有知道的告诉我。。。
- java如何实现多线程之间的通讯和协作?
共享变量和锁
锁
- 什么是可重入锁(ReentrantLock)?
已经持有这把锁的对象可以再次加锁,内部会维护一个共享变量,加锁+1 解锁-1,为0时才能被别人抢占。 所以使用重复加锁也要重复解锁
Jvm负责跟踪对象被加锁的次数,线程第一次给线程加锁的时候,计数变为1,每当这个相同的线程在此对象上再次获得锁时候,计数会增加,每当任务离开时,计数递减,当计数为0时,锁被完全释放
- 当一个线程进入某个对象的一个synchronized的实例方法后,其它线程是否可进入此对象的其它方法?
不可进入synchroized修饰的方法
- synchronized和java.util.concurrent.locks.Lock的异同?
都是可重入锁,java后面优化后性能和功能差距不大,lock可以实现公平锁
- 乐观锁和悲观锁的理解及如何实现,有哪些实现方式?
乐观锁,每次操作时不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止
悲观锁是会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。
乐观锁可以使用volatile+CAS原语实现
悲观锁可以使用synchronize的以及Lock。
并发框架
- SynchronizedMap和ConcurrentHashMap有什么区别?
前者只是在方法上加入synchronized 锁的粒度很大 ,后者是在map的”桶“上加锁,如果两个线程操作不是一个”桶“则可以同时进行 。性能更好
- CopyOnWriteArrayList可以用于什么应用场景?
在写时复制。修改时不影响读,读多写少的情况。CopyOnWriteArrayList这个容器适用于多读少写…读写并不是在同一个对象上。在写时会大面积复制数组,所以写的性能差,在写完成后将读的引用改为执行写的对象
线程安全
- 什么叫线程安全?servlet是线程安全吗?
线程安全:就是所有情况都在”掌控”之中,所有情况都是可以预料和可以接受的 。servlet不是线程安全,但是spring的编程模式让我们尽量远离线程安全问题。
- 同步有几种实现方法?
synchronized , 锁 ,使用共享变量做标示线程自旋等待
- volatile有什么用?能否用一句话说明下volatile的应用场景?
内存屏障,防止指令重排,保证可见性,一个线程的修改一定会被其他线程看见
遵循happens-before 场景 :修饰共享变量
- 请说明下java的内存模型及其工作流程。
- 为什么代码会重排序?
优化性能,操作是在栈上进行,可以连续操作同一个变量,减少弹入弹出操作
对cpu 的缓存行也友好
并发容器和框架
- 如何让一段程序并发的执行,并最终汇总结果?
使用CyclicBarrier 和CountDownLatch都可以 可以用join,或者条件变量
使用CyclicBarrier 在多个关口处将多个线程执行结果汇总
CountDownLatch 在各线程执行完毕后向总线程汇报结果
- 如何合理的配置java线程池?如CPU密集型的任务,基本线程池应该配置多大?IO密集型的任务,基本线程池应该配置多大?用有界队列好还是无界队列好?任务非常多的时候,使用什么阻塞队列能获取最好的吞吐量?
配置线程池时CPU密集型任务可以少配置线程数,大概和机器的cpu核数相当,可以使得每个线程都在执行任务
IO密集型时,大部分线程都阻塞,故需要多配置线程数,2*cpu核数
有界队列和无界队列的配置需区分业务场景,一般情况下配置有界队列,在一些可能会有爆发性增长的情况下使用无界队列。
任务非常多时,使用非阻塞队列使用cas操作替代锁可以获得好的吞吐量。
生产者负责notify,消费者需要wait,条件是有无元素
- 多读少写的场景应该使用哪个并发容器,为什么使用它?比如你做了一个搜索引擎,搜索引擎每次搜索前需要判断搜索关键词是否在黑名单里,黑名单每天更新一次
CopyOnWriteArrayList这个容器适用于多读少写…读写并不是在同一个对象上。在写时会大面积复制数组,所以写的性能差,在写完成后将读的引用改为执行写的对象
Java中的锁
- 如何实现乐观锁(CAS)?如何避免ABA问题?
读取内存值
比较内存值和期望值
替换内存值为要替换值
带参数版本来避免aba问题,在读取和替换的时候进行判定版本是否一致
- 读写锁可以用于什么应用场景?
多读少写,读写锁支持多个读操作并发执行,写操作只能由一个线程来操作
读写锁适用于读操作>>写操作,同时允许多个线程同时读,读读允许,读写互斥,写写互斥。这样的场景比较常见,比如,一个文件,我们可以要求同时读,但是不能读写或者写写同时发生。缓存Cache也可以实现同时读,互斥写。对于lucene这样的文本检索系统,也可以使用读写锁实现更新索引文件上写锁,读取索引加读锁。
- 什么时候应该使用可重入锁?
重入锁指的是在某一个线程中可以多次获得同一把锁,在线程中多次操作有锁的方法。
1.公平锁
2.多路条件,比如我要实现5个线程的同步,A->B->C->D->E->A,可以使用一个Lock,5个Condition来控制5个线程的步调
3.实现轮询,中断的功能,只听说过,没用过。
- 什么场景下可以使用volatile替换synchronized?
只需要保证共享资源的可见性的时候可以使用volatile替代,synchronized保证可操作的原子性一致性和可见性。
volatile适用于新值不依赖于就值的情形。
并发工具
- 如何实现一个流控程序,用于控制请求的调用次数?