搞不拎清的锁

Java锁

  • 公平锁/非公平锁

    公平锁指多个线程按照申请锁的顺序来获取锁

    非公平锁指多个线程不按照顺序获取锁,有可能后申请的线程比先申请的线程先获得锁。

    • 吞吐量比公平锁大,但是有可能造成优先级翻转或者饥饿现象
    new ReentrantLock() // 非公平锁
    new ReentrantLock(true) // 公平锁
    synchronized // 非公平锁
    

在这里插入图片描述

  • 可重入锁

    又名递归锁,指同一个线程在外层方法获取锁的时候,进入内层方法会自动获取锁。

    ReentrantLock 和 synchronized 都是可重入锁。

    synchronized 实现可重入锁的原理: 是加锁的时候会将线程写入到 markword中,此时会被升级为偏向锁。

    reentrantLock 实现可重入锁的原理:

    • 非公平锁
     static final class NonfairSync extends Sync {
            private static final long serialVersionUID = 7316153563782823691L;
    
            /**
             * Performs lock.  Try immediate barge, backing up to normal
             * acquire on failure.
             */
            final void lock() {
                // 如果获取到了锁 就会把当前的state 记录为 1
                // 假设第二个线程进来,期待由0改为1 ,此时由于记录已经为1 所以不会成功
                if (compareAndSetState(0, 1))
                    // 会往一个地方记录当前线程
                    setExclusiveOwnerThread(Thread.currentThread());
                else
                    /**
    Acquires in exclusive mode, ignoring interrupts. Implemented by 	invoking at least once tryAcquire, returning on success. Otherwise the thread is queued, possibly repeatedly blocking and unblocking, invoking tryAcquire until success. This method can be used to implement method Lock.lock.
    Params:
    arg – the acquire argument. This value is conveyed to tryAcquire but is otherwise uninterpreted and can represent anything you like.
                    */
                    acquire(1);
            }
    
            protected final boolean tryAcquire(int acquires) {
                // 调用非公平try
                return nonfairTryAcquire(acquires);
            }
        }
    
    // 调用一个CAS操作 
    protected final boolean compareAndSetState(int expect, int update) {
            // See below for intrinsics setup to support this
            return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
        }
    
    
    final boolean nonfairTryAcquire(int acquires) {
        // 记录当前线程
                final Thread current = Thread.currentThread();
        // 获取state
                int c = getState();
        // 如果获取到了
                if (c == 0) {
                    // 就直接拿到锁了
                    if (compareAndSetState(0, acquires)) {
                        setExclusiveOwnerThread(current);
                        return true;
                    }
                }
        // 如果当前 被锁了,校验下当前的线程 和被锁的线程是不是一致。
                else if (current == getExclusiveOwnerThread()) {
                    int nextc = c + acquires;
                    if (nextc < 0) // overflow
                        throw new Error("Maximum lock count exceeded");
                    setState(nextc);
                    return true;
                }
        //否则return false
                return false;
            }
    
    // AbstractQueuedSynchronizer
    public final void acquire(int arg) {
        // 会调用子类的tryAcquired
            if (!tryAcquire(arg) &&
                acquireQueued(addWaiter(Node.EXCLUSIVE), arg)
               )
                selfInterrupt();
        }
    
    • 公平锁

      static final class FairSync extends Sync {
          private static final long serialVersionUID = -3000897897090466540L;
          final void lock() {
              // 通过父类调用子类的 tryAcquire
              acquire(1);
          }
      
          /**
           * Fair version of tryAcquire.  Don't grant access unless
           * recursive call or no waiters or is first.
           */
          protected final boolean tryAcquire(int acquires) {
              // 记录下当前的线程
              final Thread current = Thread.currentThread();
              int c = getState();
              if (c == 0) {
                  // 获取到锁
                  if (!hasQueuedPredecessors() &&
                      // 占锁成功
                      compareAndSetState(0, acquires)) {
                      setExclusiveOwnerThread(current);
                      return true;
                  }
              }
              else if (current == getExclusiveOwnerThread()) {
                  int nextc = c + acquires;
                  if (nextc < 0)
                      throw new Error("Maximum lock count exceeded");
                  setState(nextc);
                  return true;
              }
              return false;
          }
      }
      
      public final boolean hasQueuedPredecessors() {
          // The correctness of this depends on head being initialized
          // before tail and on head.next being accurate if the current
          // thread is first in queue.
          Node t = tail; // Read fields in reverse initialization order
          Node h = head;
          Node s;
          // 头 不等于 尾 说明队列中有进程
          return h != t &&
              // 获取head.next 并且 取出来的进程 需要等于当前进程才会返回 true
              ((s = h.next) == null || s.thread != Thread.currentThread());
      }
      
  • 独享锁/共享锁

    独享锁指该所锁一次只能被一个线程所持有

    共享锁是指该锁可以被多个线程所持有

    ReentrantLock就是一个独享锁

    ReentrantReadWriteLock 其读锁是共享,写锁是独享。

    ReentrantReadWriteLock.ReadLock
    public void lock() {
     // 共享
     sync.acquireShared(1);
    }
    
    ReentrantReadWriteLock.WriteLock
    public void lock() {
     // 独占
     sync.acquire(1);
    }
    

    // 独占锁

protected final boolean tryAcquire(int acquires) {
            /*
             * Walkthrough:
               如果读或者写的值为非 0 并且 该线程是一个不同的线程 则获取锁失败
             * 1. If read count nonzero or write count nonzero
             *    and owner is a different thread, fail.
               计数饱和 65535
             * 2. If count would saturate, fail. (This can only
             *    happen if count is already nonzero.)
               否则的话,这个线程有资格锁定 或者它是一个可重入锁。如果是这样的话,更新状态并更新thread
             * 3. Otherwise, this thread is eligible for lock if
             *    it is either a reentrant acquire or
             *    queue policy allows it. If so, update state
             *    and set owner.
             */
    		// 获取当前线程
            Thread current = Thread.currentThread();
    		// 获取状态
            int c = getState();
            // 获取写数量 独占锁的重入数
            int w = exclusiveCount(c);
    		// 状态不为0 此时已经被锁
            if (c != 0) {
                // (Note: if c != 0 and w == 0 then shared count != 0)
                // 状态被锁,但是 写锁又等于0 说明此时有读锁情况 有读锁情况就不允许写锁
                // 或者 w != 0 当前的这个线程不是占锁的那个线程
                if (w == 0 || current != getExclusiveOwnerThread())
                    return false;
                // 是锁定的线程的话,独占锁的重入数需要加上去 如果大于最大量65535
                if (w + exclusiveCount(acquires) > MAX_COUNT)
                    throw new Error("Maximum lock count exceeded");
                // Reentrant acquire
                // 重入
                setState(c + acquires);
                // 可以运行后续操作
                return true;
            }
    		// 状态不为0 此时没有锁
    		// 写锁是否需要阻塞
            if (writerShouldBlock() ||
                !compareAndSetState(c, c + acquires))
                return false;
    		// 正常获取到锁,把自己的线程写入到变量中
            setExclusiveOwnerThread(current);
            return true;
        }
// wirterShouldBlock() 怎么才会返回 true
static final class FairSync extends Sync {
        private static final long serialVersionUID = -2274990926593161451L;
        final boolean writerShouldBlock() {
            return hasQueuedPredecessors();
        }
        final boolean readerShouldBlock() {
            return hasQueuedPredecessors();
        }
    }

public final boolean hasQueuedPredecessors() {
        // The correctness of this depends on head being initialized
        // before tail and on head.next being accurate if the current
        // thread is first in queue.
        Node t = tail; // Read fields in reverse initialization order
        Node h = head;
        Node s;
    	// 头不等于尾 队列中已经有数据
        return h != t &&
            // 下一个节点不为空 或者 下一个节点中的线程 不是当前线程
            ((s = h.next) == null || s.thread != Thread.currentThread());
    }

// 共享锁

protected final int tryAcquireShared(int unused) {
    /*
     * Walkthrough:
     // 如果写锁被其他线程占用了,retrun false
     * 1. If write lock held by another thread, fail.
     
     * 2. Otherwise, this thread is eligible for
     *    lock wrt state, so ask if it should block
     *    because of queue policy. If not, try
     *    to grant by CASing state and updating count.
     *    Note that step does not check for reentrant
     *    acquires, which is postponed to full version
     *    to avoid having to check hold count in
     *    the more typical non-reentrant case.
     // 
     * 3. If step 2 fails either because thread
     *    apparently not eligible or CAS fails or count
     *    saturated, chain to version with full retry loop.
     */
    // 获取当前线程
    Thread current = Thread.currentThread();
    // 获取当前线程状态
    int c = getState();
    // 如果写锁线程数 != 0 ,且独占锁不是当前线程则返回失败
    if (exclusiveCount(c) != 0 &&
        getExclusiveOwnerThread() != current)
        return -1;
    // 读锁数量
    int r = sharedCount(c);
    // 读锁是否需要阻塞
    if (!readerShouldBlock() &&
        // 读锁数量小于 65535
        r < MAX_COUNT &&
        // 设置读锁状态成功
        compareAndSetState(c, c + SHARED_UNIT)) {
        // 第一个进来的
        if (r == 0) {
            firstReader = current;
            firstReaderHoldCount = 1;
        } else if (firstReader == current) {
            // 重入的情况
            firstReaderHoldCount++;
        } else {
            // 获取计数器
            HoldCounter rh = cachedHoldCounter;
            // 计数器为空或者 计数器中的tid 不是当前线程
            if (rh == null || rh.tid != getThreadId(current))
                // 获取当前线程对应的计数器
                cachedHoldCounter = rh = readHolds.get();
            else if (rh.count == 0)
                // 向对应的线程技术器中设值
                readHolds.set(rh);
            rh.count++;
        }
        return 1;
    }
    return fullTryAcquireShared(current);
}

// 用于记录每个线程的重入数
// The number of reentrant read locks held by current thread. Initialized only in constructor and readObject. Removed whenever a thread's read hold count drops to 0.
private transient ThreadLocalHoldCounter readHolds;

在线程持有读锁的情况下,该线程不能取得写锁(因为获取写锁的时候,如果发现当前的读锁被占用,就马上获取失败,不管读锁是不是被当前线程持有)

在线程持有写锁的情况下,该线程可以继续获取读锁(获取读锁时如果发现写锁被占用,只有写锁没有被当前线程占用的情况才会获取失败)。

因为当线程获取读锁的时候,可能有其他线程同时也在持有读锁,因此不能把获取读锁的线程“升级”为写锁;而对于获得写锁的线程,它一定独占了读写锁,因此可以继续让它获取读锁,当它同时获取了写锁和读锁后,还可以先释放写锁继续持有读锁,这样一个写锁就“降级”为了读锁。

  • 互斥锁/读写锁

  • 乐观锁/悲观锁

    乐观锁 顾名思义比较乐观,每次拿数据时都认为别人不会修改,所以不上锁。等到数据更新了之后,比较下,如果数据更改了,再说。CAS是乐观锁的一种实现方式

    悲观锁:比较悲观,认为每个人都会更改数据,所以每次都会上锁。 synchronized就是悲观锁。

  • 分段锁

    一种锁的实现方式,用于高并发
    jdk 1.7 中 ConcurrentHashMap 中就使用了分段锁的理念,用于提升新能

  • 偏向锁、轻量级锁、重量级锁

    这里是针对synchronized的一个锁升级的过程

    1、最开始处于无锁状态

    2、如果此时有线程过来竞争,状态会升级为偏向锁 状态,此时会将线程记录下来,下次该线程执行方法时可以直接执行,不需要竞争锁(可重入)。

    3、第二个线程也参与竞争,此时状态继续升级 升级为 轻量级锁。该线程会通过自旋的方式尝试获取锁

    4、如果第二个线程迟迟获取不到锁,此时状态会继续升级为 重量级锁,重量级锁会让其他线程进入等待。

  • 自旋锁

    是指当一个线程在获取锁的时候,如果锁已经被其它线程获取,那么该线程将循环等待,然后不断的判断锁是否能够被成功获取,直到获取到锁才会退出循环。可以通过循环,利用CAS实现

AQS-AbstractQueuedSynchronizer

抽象队列同步器,定义了一套多线程访问共享资源的同步器框架

CAS:Compare And Swap

CAS 操作包含三个操作数 —— 内存位置(V)、预期原值(A)和新值(B)。 如果内存位置的值与预期原值相匹配,那么处理器会自动将该位置值更新为新值 。否则,处理器不做任何操作。无论哪种情况,它都会在 CAS 指令之前返回该位置的值。(在 CAS 的一些特殊情况下将仅返回 CAS 是否成功,而不提取当前 值。)CAS 有效地说明了“我认为位置 V 应该包含值 A;如果包含该值,则将 B 放到这个位置;否则,不要更改该位置,只告诉我这个位置现在的值即可。

Mysql数据库锁

  • 操作粒度分类:表级锁、行级锁

    • 表级锁:每次锁住整张表。粒度大,应用在MyISAM、InnoDB中
    • 行级锁:每次锁住一行记录。粒度最小,并发度高。应用在innodb中
  • 操作的类型:读锁(S锁)、写锁(X锁)

    • 读锁:共享锁,针对同一份数据,多个读操作可以同时进行而不相互影响

    • 写锁:排他锁,当前写操作没有完成前,它会阻断其他写锁

    • IS锁、IX锁:意向读锁、意向写锁 (属于表级) S、X 主要针对行级锁。

      在对表记录添加S或者X锁之前,会先对表添加IS或者IX锁。

      S锁:事务A对记录添加了S锁,可以对记录进行读操作,不能做修改。其他事务可以对该记录追加S锁,但是不能追加X锁,需要追加X锁,需要等记录的S锁全部释放

      X锁:事务A对记录添加了X锁,可以对记录进行读和修改操作,其他事务不能对记录做读和修改的操作。

  • 操作性能:乐观锁、悲观锁

    • 乐观锁:一般的实现方式是对数据版本进行比对,在数据更新提交的时候冲突检测,如果发现冲突了,则提示错误信息。
    • 悲观锁:在对一条数据修改的时候,为了避免同时被其他人修改,在修改数据之前先锁定,再修改的控制方式。共享锁和排他锁是悲观锁的不同实现,但都属于悲观锁范畴。

分布式锁

参考资料:

https://www.cnblogs.com/hustzzl/p/9343797.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值