Synchronized和ReentrantLock实现原理
synchronized(用来实现互斥锁mutex)
synchronized作用在代码块时,底层是通过monitorenter,monitorexit指令来实现
- monitorenter:每个对象都有监视锁(monitor),被占用对象就被锁定,线程执行monitorenter尝试获得monitor的所有权。初始monitor的进入数为0,该线程第一次进入置数为1,同一线程可重复进入数+1,其他进程想进入被阻塞直到数为0。
- monitorexit:指令执行时,monitor进入数字减1,若此时数为0,那线程退出monitor,不再是该monitor的持有者。其他线程可尝试获得权限。
ReentrantLock
基于AQS(AbstractQueueSynchronized)抽象类,里面有两个队列
AQS管理两队列间等待状态-唤醒的工作。
- 同步队列:双向链表,存储等待状态线程,等待唤醒获取锁
- 条件队列:单向链表,存储等待状态线程,唤醒后加入同步队列队尾
ReentrantLock实例化AQS抽象类,重写获取和释放锁(tryAcquire,tryRelease)的方式,管理state,实现lock和unlock
除去Synchronized和ReentrantLock,怎么保护线程安全
1.volatile:该关键字为域变量的访问提供了一种免锁机制,相当于告诉jvm该域可能被其他线程更新,每次使用该域都要重新计算
2.原子变量
Java乐观锁和悲观锁,公平锁和非公平锁
- 悲观锁:总是假设最坏的情况,每次拿数据时防止被人修改都上锁,syn或lock实现
- 乐观锁:每次拿数据都认为别人不会修改,不上锁
- 公平锁:每次有线程抢占锁,先检查一边有无等待队列(eg:syn,ReentrantLock)
- 非公平锁:实现时多次强调随机抢占(ReentrantLock)
最大区别:新晋获取锁的进程有多次机会去抢占锁,但加入等待队列后和公平锁一致。
线程池
针对问题:程序要创建大量生存期很短暂的线程时,启动新线程成本高(涉及与OS交互),使用线程池能很好的提升性能。
原理:线程池在系统启动时即创建大量空闲线程,程序将一个Runnable或Callable对象传给线程池,线程池会启动一个空闲的线程来执行run()或call()方法,执行结束后,空闲线程不死亡,而是再次返回线程池等待下次调用
newCachedThreadPool();newFixedThreadPool(int Threads);newSingleExector()
线程池状态(1->2,3->4->5)
1.Running:能接收新提交任务,呢嗯处理阻塞队列中任务
2.ShutDown:关闭状态,不再接收新提交任务
3.Stop:不能接收处理新任务,终端正在处理线程
4.TidYing:若所有任务终止,有效线程数为0,该线程池调用terminated()进入5状态
4.Terminated:什么也不做
线程池大小
分CPU密集型(CPU核心数+1),和IO密集型(2*CPU核心数)
6大参数
1.核心工作线程数(corePoolSize)
2.最大线程数(maximumPoolSize)
3.多余线程存活时间(keepAliveTime)
4.队列(workQueue)
5.线程创建工厂(threadFactory)
6.拒绝策略(handler)
拒绝策略
线存池任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来会采取拒绝策略
AbortPoilcy(丢弃任务,抛出RejectedExecutionException异常)
DiscardPoilcy(丢弃任务,不跑出异常)