1. synchronized 四种状态以及锁升级过程
无锁 -> 偏向锁 -> 轻量级锁(cas) -> 重量级锁
- 当只有一个线程进行加锁的时候,默认会使用偏向锁
- 一旦有线程竞争就会升级成轻量级锁(未获得锁的线程会通过自旋等待获取锁,占用cpu资源)
- 默认自旋超过10次还未获取锁会升级为重量级锁
2. synchronized 和ReentrantLock区别
- synchronized使用的是锁升级的方式加锁,ReentrantLock使用的是cas
- ReentrantLock可以使用tryLock进行尝试加锁,synchronized只能等待获取锁
- ReentrantLock需要手动加锁(lock)解锁(unlock),synchronized代码执行完成自动解锁
- ReentrantLock可以创建不同的Condition,实现不同的等待队列,更好的实现生产者消费者模式
3. volatile有什么作用,分别是怎么实现的
- 线程之间可见(缓存一致性协议 EMSI)
- 禁止指令重排序(内存屏障 ll ss ls sl)
4. cas
Compare and Swap 比较再交换
CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。
自旋的ABA 问题如何解决?基础类型的操作如果没特殊要求可以不用进行处理,对象或有特殊要求的可以使用版本号进行比较
5. 线程的状态
- new 还未start的线程
- RUNNABLE 正在执行的线程 有可能是正在等待cpu的调度,有可能是正在运行
- BLOCKED 未获取到锁的
- WAITING 等待 wait join LockSupport.park
- TIMED_WAITING 有时效性的等待 sleep(long) wait(long) join(long) 等
- TERMINATED 执行结束
6. Runnable接口和Callable接口的区别?
- Runnable接口中的run()方法的返回值是void
- Callable接口中的call()方法是有返回值的,是一个泛型,和Future、FutureTask配合可以用来获取异步执行的结果。
7. 如何在两个线程之间共享数据
8. 单例模式中的双重锁机制(DCL),对象是否需要添加volatile修饰,作用是什么?
必须添加volatile修饰,禁止指令重排序,如果不加有可能获取到一个半初始化状态的对象,对象初始化:1.分配内存,初始化成员变量2.给成员变量赋值3.将内存地址指向对象 。如果不加volatile的话指令重排序后有可能执行顺序是1,3,2.步骤3执行过后对象就不是一个空对象,但是成员变量还未赋值,拿到的就是一个半初始化状态的对象。
9. 写一段程序,两个线程交替打印A1B2C3……
线程之间通讯
- wait notify
- LockSupport park unPark
- volatile + cas
- 使用SynchronousQueue空队列交换数据打印
10. notify 和 notifyAll有什么区别
notify唤醒一个在等待的线程,并不能指定唤醒线程,notifyAll唤醒所有等待的线程
11. 公平锁和非公平锁的区别
- 公平锁在新线程需要获取锁的时候会先检查等待队列是否有其它阻塞线程,如果有,则加入到等待队列。
优点:所有线程都会获取锁
缺点:线程唤醒的代价比较大,影响效率 - 非公平锁在新线程获取锁的时候不需要检查等待队列,直接参与竞争锁,如果能获取到就获取锁,如果获取不到,就加入等待队列
优点:可以减少cpu唤起线程的开销
缺点:可能导致某些线程一直获取不到锁
12. 共享锁,排它锁 ReadWriteLock
13. AQS
state 线程的重入数 大于0被线程所占用
node双向链表,等待队列
14. Varhandle jdk1.9新增
- 比反射速度快
- 直接操作二进制码
- 可以直接修改属性值,普通对象也可以进行原子操作,多线程安全操作
使用的cas
15. ThreadLocal
每一个线程独有一个ThreadLocalMap,确保线程之间数据互不影响。
使用场景,声明式事务管理,每一个线程独有一个数据库连接,确保每一个线程在同一个事务。
ThreadLocalMap使用弱引用,当threadlocal强引用被回收之后,map的key弱引用也会被回收,value不会被回收,因此在使用完ThreadLoca后要把相关的value remove掉,以免造成内存泄露。
16. 对象的引用类型
- 强
强引用,普通new一个对象就是强引用,当对象使用完成或者对象为空时就会被gc回收 - 软
软引用,当虚拟机内存不够时就会被gc回收,一般用作缓存使用 - 弱
弱引用,会立马被gc回收,但是同时有一个强引用指向时不会被回收,当强引用被回收后就会立马被回收,使用场景,ThreadLocalMap的key - 虚
虚引用,当内存被回收时会被加入到一个队列里面,可以对队列进行监听以达到获取通知的作用
使用场景:管理堆外内存,gc不能回收堆外内存,获取到回收通知后手动对堆外内存进行回收。
netty使用的就是堆外内存
java的unsafe类可以直接操作堆外内存
17. 容器(Collection Map Queue)
- HashTable HashMap SynchronizedMap LinkedHashMap TreeMap ConcurrentHashMap ConcurrentSkiplistmap
- Vector ArrayList LinkedList SynchronizedList CopyOnWriteArrayList
- HashSet TreeSet
- ConcurrentLinkedQueue LinkedBlockingQueue ArrayBlockingQueue PriorityQueue DelayQueue SynchronousQueue TransferQueue
18. List和Queue的区别
Queue提供了对多线程操作的一些api,类如offer poll(取出一个,从队列里面移除) peek(取出一个,不从队列里面移除)
BlockQueue put阻塞,当队列已满时会阻塞,offer 当队列已满时会返回false,可以设置等待时间,add 当队列满了会抛出异常
19. CompletableFuture
管理多个线程的返回结果
allof 所有线程都获取到结果
anyof 任意一个线程获取到结果
……
20. callable future futuretask
21. 线程池7个参数
- corePoolSize
核心线程数 - maximumPoolSize
最大线程数 - keepAliveTime
空闲线程会根据存活时间进行销毁,默认是非核心线程数,设置了allowCoreThreadTimeOut时,核心线程也会销毁 - unit
时间单位 - workQueue
等待队列,当最大线程数不够用时,会将线程加入等待队列
LinkedBlockingQueue Integer.MAX 长度
SynchronousQueue 空队列
ArrayBlockingQueue 固定长度 - threadFactory
线程的创建工厂 定义线程的组和名字等 创建线程一定要自定义一个有意义的名字,以方便jstack 排查问题 - RejectedExecutionHandler 拒绝策略,当线程数和等待队列都满了之后的拒绝策略,jdk 提供了四种
AbortPolicy 直接抛出异常
DiscardPolicy 什么事情都不干,直接丢弃掉
DiscardOldestPolicy 丢弃掉队列中最早的
CallerRunsPolicy 交给发起者执行,比如main线程调用,就交给main线程执行
实际场景中一般都是自定义,比如存储到redis,mq,数据库等,避免数据丢失
网友评论