1.乐观锁和悲观锁
乐观锁:读多写少,读数据默认其他线程不会修改该数据,默认不上锁,但是在更新的时候在该期间判断数据有没有更新。(在写时读取版本号,若发生改变,则重新读取--比较--修改)
悲观锁:写少读多,每次读写操作的时候都会上锁。
如Synchronized是悲观锁,AQS框架下的锁(ReenTrantLock)则先尝试cas乐观锁去获取锁,获取不到则会转换为悲观锁
注:AQS框架指提供了一种实现阻塞锁和一系列依赖的FIFO等待队列同步器框架
2.自旋锁
持有锁的线程在很短的时间内释放资源,等待竞争的线程不需要进入阻塞状态,只需要等待一定的时间(自旋)等持有锁的线程释放锁即可立即获取锁,避免用户进行和内河的切换消耗
注:1.如果在设置等待的最大时间内仍然没有释放锁,则会停止自旋进入阻塞状态
2.若持有锁的线程需要长时间占有锁,则不适合用自旋锁
3.公平锁和非公平锁
公平锁:公平锁指的是锁的分配机制是公平的。加锁前检查是否有排队的等待线程,先到先得
非公平锁:按随机、就近原则分配的锁机制称为不公平锁。加锁前不考虑排队等待问题,直接尝试获取,获取不到自动到队尾等待,
注:1.非公平锁性能比公平锁高5到10倍。因为公平锁需要在多核的情况下维护一个队列
2.synchronized是非公平锁,ReentrantLock默认的lock()方法采用的是非公平锁
4.共享锁和独占锁
独占锁:独占锁是一种悲观的加锁策略,在读/读操作也是只允许一个线程获取锁
共享锁:共享锁是一种乐观的加锁策略,允许多个执行读操作的线程同时访问共享资源。(ReadWriteLock)
5.锁状态
锁的状态总共有四种
无锁状态、偏向锁、轻量级锁和重量级锁
偏向锁:偏向锁是为了在无多线程竞争的情况下尽量减少不必要的轻量级锁执行路径而造成的性能消耗。
注:轻量级锁的获取和释放依赖多次CAS原子指令,而潘向所只需要在置换ThreadID的时候依赖一次原子指令。偏向锁在只有一个线程执行同步块的时候进一步提高性能。
轻量级锁:在没有多线程竞争的前提下,减少传统重量级锁使用的性能消耗。轻量级锁适用的场景是线程交替执行同步块的情况。
注:如果存在同一时间访问同一锁的情况,就会导致轻量级锁膨胀成重量级锁。
重量级锁:依赖于操作系统Mutex Lock所实现的锁我们称之为重量级锁
注:操作系统实现线程之间的切换需要从用户态转换到核心态,成本较高,这就是Synchronized效率较低的原因
6.读写锁(ReadwriteLock)
读写锁分为读锁和写锁,多个读锁不互斥,写锁互斥。
7.可重入锁
可重入锁也叫他、递归锁,指的是同一线程外层函数获得锁之后,内层递归函数仍有获取该锁的代码,但不受该锁的影响。
注:在java环境下ReentrantLock和synchronized都是可重入锁
8.ReentrantLock和synchronized
synchronized:它可以把任意一个非Null的对象当做锁。属于独占式的悲观锁,同时也是可重入锁。
synchronized核心组件:
1)Wait Set:被阻塞的线程放置的位置
2)Contention List;竞争队列。所有请求锁的线程首先会被放置再这里
3)Entry List:有资格成为候选资格的线程放置的位置
4)OnDeck:任意时刻竞争锁资源的线程被称为OnDeck
5)Owner:当前已经获取到所有资源的线程被称为Owner
6)!Owner:当前释放锁的线程
synchronized实现流程图:
注:1.作用在方法时:锁住的是对象的实例
2作用于静态方法时,锁住的是class实例(class数据存储在永久代,数据全局共享,相当于全局锁)
3.所用于实例时,锁住的是以该对象为锁的代码块。
ReentrantLock:继承Lock接口实现接口中定义的方法,除了能完成synchronized锁完成的全部工作外,还提供了诸如响应中断锁、可轮询锁请求、定时锁等,避免多线程造成的死锁。
注:ReentrantLock在构造函数中 提供了是否公平锁的初始化
例如:Lock lock=new ReentrantLock(true);//公平锁
9.ReentrantLock和synchronized的区别
共同点:1.都是可重入锁。同一线程可以多次获得一个锁。
2.都保证了可见性和互斥性
3.都是用来协调多线程对对象和变量的访问
不同点;1.ReentrantLock显示获得、释放锁,synchronized隐式获得释放锁
2.ReentrantLock可响应中断、释放锁,能够对锁状态控制管理,诸如响应中断锁、可轮询锁请求、定时锁等。
3.ReentrantLock是API级别的,synchronized是JVM级别的
4. ReentrantLock可以实现公平锁
5.底层实现不一样,ReentrantLock是乐观策略,synchrobnized采用的悲观策略
6.ReentrantLock可以提高多个线程测读操作效率
10.锁优化
1. 减少锁持有的时间
只用在有线程安全要求的程序上加锁
2.减少锁粒度
将被多个线程访问的的对象拆分,增加并行度,降低锁的竞争
3.锁分离
常见的锁分离就是读写锁(ReadWriteLock),根据功能进行分离成读锁和写锁,既保证了线程安全又提高了性能。只要操作互不影响,锁就可以分离 。
4.锁粗化
通常情况下,为了保证多线程间的有效并发,会要求每个线程持有锁的时间尽量短,即在使用完公共资源后,应该立即释放锁。如果对同一个锁不停的进行请求、同步和释放,其本身也会消耗系统宝贵的资源。
5.锁消除
锁消除是在编译级别的事情,在即时编译其中 ,如果发现不能共享的对象,则可以消除这些对象的操作。多数因为程序不规范引起的。
来源:oschina
链接:https://my.oschina.net/u/4339309/blog/4173287