Java多线程(九)---Java中的锁---重入锁Reentr

2019-02-14  本文已影响275人  brock

1 公平和非公平

2 实现重进入

2.1 非公平性(默认的)获取锁

  final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    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)
                    throw new Error("Maximum lock count exceeded");
            setState(nextc);
            return true;
    }
    return false;
}

该方法增加了再次获取同步状态的处理逻辑:

2.2 非公平性(默认的)释放锁

protected final boolean tryRelease(int releases) {
    int c = getState() - releases;
    if (Thread.currentThread() != getExclusiveOwnerThread())
            throw new IllegalMonitorStateException();
    boolean free = false;
    if (c == 0) {
            free = true;
            setExclusiveOwnerThread(null);
    }
    setState(c);
    return free;
}

2.3 公平锁

 ReentrantLock的tryAcquire方法  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;
}

该方法与nonfairTryAcquire(int acquires)比较,唯一不同的位置为判断条件多了hasQueuedPredecessors()方法,即加入了同步队列中当前节点是否有前驱节点的判断,如果该方法返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。

3公平与非公平对比

public class FairAndUnfairTest {
    private static Lock    fairLock        = new ReentrantLock2(true);
    private static Lock    unfairLock    = new ReentrantLock2(false);
    @Test
    public void fair() {
            testLock(fairLock);
    }
    @Test
    public void unfair() {
            testLock(unfairLock);
    }
    private void testLock(Lock lock) {
            // 启动5个Job(略)
    }
    private static class Job extends Thread {
            private Lock    lock;
            public Job(Lock lock) {
                    this.lock = lock;
            }
            public void run() {
                    // 连续2次打印当前的Thread和等待队列中的Thread(略)
            }
    }
    private static class ReentrantLock2 extends ReentrantLock {
            public ReentrantLock2(boolean fair) {
                    super(fair);
            }
            public Collection<Thread> getQueuedThreads() {
                    List<Thread> arrayList = new ArrayList<Thread>(super.
                    getQueuedThreads());
                    Collections.reverse(arrayList);
                    return arrayList;
            }
    }
}

分别运行fair()和unfair()两个测试方法

image

测试运行时系统线程上下文切换的次数
下面运行测试用例(测试环境:ubuntu server 14.04 i5-34708GB,测试场景:10个线程,每个线程获取100000次锁),通过vmstat统计测试运行时系统线程上下文切换的次数

image

在测试中公平性锁与非公平性锁相比,总耗时是其94.3倍,总切换次数是其133倍。可以看出,公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量

参考

《java并发编程的艺术》

上一篇下一篇

猜你喜欢

热点阅读