线程锁入门

1、自旋锁

       自旋锁是指当一个线程尝试获取某个锁时,如果该锁已被其他线程占用,就一直循环检测锁是否被释放,而不是进入线程挂起或睡眠状态。自旋锁适用于锁保护的临界区很小的情况,临界区很小的话,锁占用的时间就很短。

线程锁入门

2、排队自旋锁

       解决上面的公平性问题: 没法保证公平性,不保证等待进程/线程按照FIFO顺序获得锁。

       锁拥有一个服务号,表示正在服务的线程,还有一个排队号;每个线程尝试获取锁之前先拿一个排队号,然后不断轮询锁的当前服务号是否是自己的排队号,如果是,则表示自己拥有了锁,不是则继续轮询。当线程释放锁时,将服务号加1,这样下一个线程看到这个变化,就退出自旋。

线程锁入门

3、CLH锁

        https://blog.****.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、可重入锁

         就是一个线程在获取了锁之后,再次去获取了同一个锁,这时候仅仅是把状态值进行累加。