JAVA并发篇_公平锁与非公平锁

Java
351
0
0
2022-12-19
标签   Java并发
简单的来说,如果一个线程组里,能保证每个线程都能拿到锁,那么这个锁就是公平锁。相反,如果保证不了每个线程都能拿到锁,也就是存在有线程饿死,那么这个锁就是非公平锁。

一、引入概念

1、公平锁:

多个线程按照申请锁的顺序去获得锁,线程会直接进⼊队列去排队,永远都是队列的第⼀位才能得到锁。

优点:所有的线程都能得到资源,不会饿死在队列中。

缺点:吞吐量会下降很多,队列⾥⾯除了第⼀个线程,其他的线程都会阻塞,cpu唤醒阻塞线程的

开销会很⼤。

2、⾮公平锁:

多个线程去获取锁的时候,会直接去尝试获取,获取不到,再去进⼊等待队列,如果能获取到,就直接获取到锁。

优点:可以减少CPU唤醒线程的开销,整体的吞吐效率会⾼点,CPU也不必取唤醒所有线程,会减少唤起线程的数量。

缺点:可能导致队列中间的线程⼀直获取不到锁或者⻓时间获取不到锁,导致饿死。

二、Java中的实现

如何能保证每个线程都能拿到锁呢,队列FIFO是一个完美的解决方案,也就是先进先出,java的ReenTrantLock也就是用队列实现的公平锁和非公平锁。 在公平的锁中,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个所,那么新发出的请求的线程将被放入到队列中。而非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中(此时和公平锁是一样的)。所以,它们的差别在于非公平锁会有更多的机会去抢占锁。

1、公平获取锁

java.util.concurrent.locks.ReentrantLock$FairSync.java

protected final boolean tryAcquire( int acquires) {
     final Thread current = Thread.currentThread();
     int c = getState();
     //状态为0,说明当前没有线程占有锁 
     if (c ==  0 ) {
        //如果当前线程是等待队列的第一个或者等待队列为空,则通过cas指令设置state为1,当前线程获得锁 
         if (isFirst(current) &&
             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 ;
}

2、非公平获取锁

java.util.concurrent.locks.ReentrantLock$Sync.java

 final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                //如果当前没有线程占有锁,当前线程直接通过cas指令占有锁,无视等待队列,就算自己排在队尾也是这样 
                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;
        }

三、适用场景

更多的是直接使用非公平锁:非公平锁比公平锁性能高5-10倍,因为公平锁需要在多核情况下维护一个队列,如果当前线程不是队列的第一个无法获取锁,增加了线程切换次数。