1、自旋锁
自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短。
2、排队自旋锁
解决上面的公平性问题: 没法保证公平性,不保证等待进程/线程按照FIFO顺序获得锁。
锁拥有一个服务号,表示正在服务的线程,还有一个排队号;每个线程尝试获取锁之前先拿一个排队号,然后不断轮询锁的当前服务号是否是自己的排队号,如果是,则表示自己拥有了锁,不是则继续轮询。当线程释放锁时,将服务号加1,这样下一个线程看到这个变化,就退出自旋。
3、CLH锁
https://blog.csdn.net/aesop_wubo/article/details/7533186
一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class CLHLock {
public static class CLHNode {
private boolean isLocked = true; // 默认是在等待锁
}
@SuppressWarnings("unused" )
private volatile CLHNode tail ;
private static final AtomicReferenceFieldUpdater<CLHLock, CLHNode> UPDATER = AtomicReferenceFieldUpdater
. newUpdater(CLHLock.class, CLHNode .class , "tail" );
public void lock(CLHNode currentThreadCLHNode) {
CLHNode preNode = UPDATER.getAndSet( this, currentThreadCLHNode); // 转载人注释: 把this里的"tail" 值设置成currentThreadCLHNode
if(preNode != null) {//已有线程占用了锁,进入自旋
while(preNode.isLocked ) {
}
}
}
public void unlock(CLHNode currentThreadCLHNode) {
// 如果队列里只有当前线程,则释放对当前线程的引用(for GC)。
if (!UPDATER .compareAndSet(this, currentThreadCLHNode, null)) {
// 还有后续线程
currentThreadCLHNode. isLocked = false ;// 改变状态,让后续线程结束自旋
}
}
}
4、MCS锁
一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,直接前驱负责通知其结束自旋,从而极大地减少了不必要的处理器缓存同步的次数,降低了总线和内存的开销。
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class MCSLock {
public static class MCSNode {
MCSNode next;
boolean isLocked = true; // 默认是在等待锁
}
volatile MCSNode queue ;// 指向最后一个申请锁的MCSNode
private static final AtomicReferenceFieldUpdater<MCSLock, MCSNode> UPDATER = AtomicReferenceFieldUpdater
. newUpdater(MCSLock.class, MCSNode. class, "queue" );
public void lock(MCSNode currentThreadMcsNode) {
MCSNode predecessor = UPDATER.getAndSet(this, currentThreadMcsNode);// step 1
if (predecessor != null) {
predecessor.next = currentThreadMcsNode;// step 2
while (currentThreadMcsNode.isLocked ) {// step 3
}
}
}
public void unlock(MCSNode currentThreadMcsNode) {
if ( UPDATER.get( this ) == currentThreadMcsNode) {// 锁拥有者进行释放锁才有意义
if (currentThread.next == null) {// 检查是否有人排在自己后面
if (UPDATER.compareAndSet(this, currentThreadMcsNode, null)) {// step 4
// compareAndSet返回true表示确实没有人排在自己后面
return;
} else {
// 突然有人排在自己后面了,可能还不知道是谁,下面是等待后续者
// 这里之所以要忙等是因为:step 1执行完后,step 2可能还没执行完
while (currentThreadMcsNode.next == null) { // step 5
}
}
}
currentThreadMcsNode.next.isLocked = false;
currentThreadMcsNode.next = null;// for GC
}
}
}
5、偏向锁
某一个锁被一个线程获取之后,便进入了偏向锁模式,当该线程再次请求这个锁时,就无需再进行相关的同步操作,从而节省了操作时间。但是如果在此期间,有其他线程申请了这个锁,则退出偏向锁模式。 偏向锁在竞争激烈的情况下没有太强的优化效果,因为大量的竞争会导致持有锁的线程不停地切换,锁也很难一直保持偏向模式,此时,使用偏向锁不仅不能优化程序,反而有可能降低程序性能。
6、CAS
三个参数,一个当前内存值V、旧的预期值A、即将更新的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做,并返回false。
多个线程对同一个变量一直使用CAS操作,那么会有大量修改操作,从而产生大量的缓存一致性流量,因为每一次CAS操作都会发出广播通知其他处理器,从而影响程序的性能。
7、可重入锁
就是一个线程在获取了锁之后,再次去获取了同一个锁,这时候仅仅是把状态值进行累加。