并发知识点总结
1、JAVA内存模型(注意与JVM内存模型的区别)
内存模型:定义一组规范,围绕可见性、原子性、有序性定义变量的访问方式,屏蔽软硬件差异达到内存一致性效果。
2、volatile关键字
可见性:内存屏障保证写操作立刻刷新缓存,读操作要从内存读取最新值到工作内存
禁止指令重排:(针对普通无锁的全局变量)插入内存屏障,保证先于屏障的指令先执行,后于内存屏障的后执行
3、原子性、可见性、有序性
原子性:内存模型的原子性变量操作和lock、unluck,提供给程序原使用的是synchronized(字节码指令monitorenter和monitorexit)
可见性:新值能够立刻同步到主存,读取前立刻从主存刷新,提供给程序员使用的是volatile,synchronized,final
有序性:线程内有序,线程间无序。通过,先行发生原则和,volatile,synchronized
我们必须要清楚,定义内存模型是为了解决内存一致性问题,即并发情况下工作内存到主存的问题,这在程序员层面是不可见的。换句话说,就是内存模型保证了原子性、可见性、有序性,而我们使用的关键字则是内存模型提供给上层程序员的一种控制内存的手段。我们有了内存一致性,还要进行同步控制的原因是:内存一致性解决的是内存问题,并发控制是保证程序的正确性。
4、创建线程的三种方式
- 继承Thread重写run()方法
- 实现Runnable接口,实现run()方法
- 实现Callable接口,实现call()方法
Callable泛型接口的call方法有返回值,submit提交返回一个异步执行的结果Future。
5、线程有哪些状态
- New:新建状态,还未启动
- Runnable:Java中Runnable包括运行和就绪两种状态由yield切换
- Blocked:只有synchronized关键字可以使线程进入阻塞状态
- Wait:无限等待
- Time—Wait:有限等待
- Terminate:终止
6、什么是守护线程
守护线程就是后台线程,所有的非后台线程结束虚拟机退出后台线程自动结束,后台线程的子线程是后台线程。
7、run()与start()的区别
run():就是一个普通的方法
start():启动一个线程去驱动任务,并马上返回。
8、yield,sleep,join,wait区别
yield:将当前线程从运行状态切换到就绪状态,让给同优先级的线程运行的机会
sleep:当前线程等待,但是不释放锁
join:等待子线程执行完毕,子线程即调用join的线程
wait:只能用在同步控制块中,释放锁,并且wait要重新获得获得的锁才能真正唤醒;作用是线程协作,一般放在while循环中,等待条件发生变化
9、notify与notifyAll
①notify唤醒一个线程,notifyAll唤醒等待同一个锁的所有线程
②notify要保证唤醒的线程等待相同的条件,所以notifyAll更加安全
③notify比notifyAll更加优化
10、synchronized底层实现原理
内存模型层面:lock、unlock
字节码指令:monitorenter,monitorexit
11、synchronized与volatile
①volatile修饰变量,synchronized修饰同步块
②volatile不能保证原子性,synchronized可以
③volatile不阻塞synchronized阻塞
12、synchronized与Lock与ReentrantLock
Lock是ReentrantLock的接口
①synchronized是内置关键字,ReentrantLock是java类
②ReentrantLock可以实现公平锁,默认是非公平锁
③ReentrantLock等待可中断,线程等待久了可以放弃等待
④ReentrantLock由park()阻塞原语使线程进入等待状态,synchronized使得线程进入阻塞状态
⑤ReentrantLock可以绑定多个条件,进行更加细致的同步控制。
13、实现线程安全的三种方式
- 阻塞同步,互斥同步(即加锁)
- 非阻塞同步,CAS(原子类)
- 线程本地存储:为每个线程分配一个共享变量的副本,通常都用static存储ThreadLocal对象,应用场景,数据库连接等线程私有对象。
14、CAS操作与atomic原理
compareAndSwap(o1,o2,o3)
以对变量i加1举例,o1是第一次读取i的值,o2是做CAS操作时读取i的值,o3是i+1的值,做CAS操作时比较o1与o2,若两者相等说明没有冲突可以更新i值,若不相等说明有冲突不能更新i值,采取补偿措施不断重试。
这里面有个逻辑漏洞:ABA问题,就是i的值从A改到B,又从B改到A而无法察觉,可以用AtomicStampedReference类通过版本值来解决,或者用锁。
这类比较鸡肋,一般情况下ABA问题不会影响程序的正确性,解决ABA问题锁又更加高效。
15、CountDownLatch、CyclicBarrier、Semaphore
CountDownLatch:一个锁存器,等到计数器为0之前,await方法一直等待,无法重置。
CyclicBarrier:循环屏障,参与者数目达到指定值之前(即await方法调用次数到达指定值),await方法一直等待,可以重置。
Semaphore:信号量设置n个许可,在许可可用前,acquire()等待。
16、并发集合
CopyOnWriteArrayList相当于线程安全的ArrayList
CopyOnWriteArraySet相当于线程安全的HashSet
ConcurrentHashMap相当于线程安全的HashMap
ConcurrentSkipListMap相当于线程安全的TreeMap
ConcurrentSkipListSet相当于线程安全的TreeSet
ArrayBlockingQueue是数组实现的线程安全的有界的阻塞队列
LinkedBlockingQueue是单向链表实现的(可指定大小)阻塞队列
ConcurrentLinkedQueue是单向链表实现的无界队列
17、ConcurrentHashMap JDK1.7与JDK1.8的区别
JDK1.7:将哈希表分段,即由很多segment片段组成,每一段就是一个可重入的互斥锁,HashTable锁全表
JDK1.8:使用CAS+synchronized实现线程安全,锁的粒度变成了节点,如HashMap节点超过8就会树化
18、线程池
线程池的原理:在池中维护一定数量的线程驱动任务,线程池帮助管理线程以免频繁创建回收线程。
19、线程池的状态
①Running状态调用shutdown()进入ShutDown状态:此时线程池不接受新任务,但仍旧处理已提交任务,处理完任务进入Tidying状态
②Running状态调用shutdownNow()进入STOP状态:此时线程池不接受新任务,不处理就任务,正在运行的任务中断,线程池任务为0进入Tidying状态
③Tidying状态执行钩子函数terminate()进入Terminated状态
20、线程池的相关参数
corepoolsize与maxpoolsize,前者是核心池大小,后者是最大池大小。池中线程数小于corepoolsize创建新线程处理任务,池中线程数多于corepoolsize但是小于maxpoolsize阻塞队列满在创建新线程。
keepalivetime:允许空闲线程存活的时间
21、排队的三种策略
①直接提交:阻塞队列设为0,maxpoolsize设置无限大。
②无界队列:一般corepoolsize=maxpoolsize,线程池维护固定数量线程。
③有界队列:线程池大小与队列大小难以权衡。
22、四种线程池类型
SingelThreadPool:线程池维护单个线程
FixedThreadPool:线程池维护固定数量线程
ScheduledThreadPool:线程池维护固定数量线程,线程设置在一定时延后进行
CacheThreadPool:线程池可以根据需要创建线程,空闲线程60秒后回收