锁的公平与非公平

  锁是给线程用的,线程拿到锁之后才能干活。当多个线程竞争一个锁时,同一个时间只能有一个线程脱颖而出持有锁,其他线程等该线程释放锁后发起下一轮竞争。那么这种竞争就存在公平性问题,如果是公平的竞争,那么这些线程就得有序来依次得到锁,这就需要线程们按先来后到排队,第一个线程使用完后把锁递给第二个线程,以此类推。非公平的锁是无序的,举个例子:线程甲持有锁,后面线程乙跑过来争,发现被甲拿了就去睡觉(休眠或者被挂起)了,等线程甲释放锁通知乙来取;过了会儿,乙被唤醒时,刚好线程丙半路杀过来了,把锁拿去用了,而乙还在迷糊着,等丙释放锁时乙刚好完全醒了拿到了锁。

  从上面可以看到,如果竞争很激烈,锁被持有时间短,那么非公平锁能充分利用时间,公平锁反而因为线程的切换浪费了时间。反之,如果线程持有锁的时间长,那么非公平锁会被频繁请求,线程重试次数多,做了很多无用功,公平锁按部就班传递锁。内置锁只能是非公平的,显式锁可以自己定义,下面看个例子:

package com.wulinfeng.concurrent;

import java.util.concurrent.locks.ReentrantLock;

public class FairAndUnfairLock {

    public void doSomething(boolean isFair) {
        ReentrantLock lock = null;
        if (isFair) {
            lock = new ReentrantLock(true); // 公平锁
            printThreadInfo(lock);
        } else {
            lock = new ReentrantLock(); // 非公平锁
            printThreadInfo(lock);
        }

    }

    private void printThreadInfo(ReentrantLock lock) {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "获取锁定.");
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        final FairAndUnfairLock fairOrUnfair = new FairAndUnfairLock();
        Thread[] threadArray = new Thread[20];

        System.out.println("非公平锁开始:");

        // 非公平锁
        for (int i = 0; i < 20; i++) {
            threadArray[i] = new Thread(new Runnable() {

                @Override
                public void run() {
                    fairOrUnfair.doSomething(false);
                }
            }, "wulinfeng-" + i);
        }
        for (int i = 0; i < 20; i++) {
            threadArray[i].start();
        }

        // 等非公平锁跑完
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("公平锁开始:");

        // 公平锁
        for (int i = 0; i < 20; i++) {
            threadArray[i] = new Thread(new Runnable() {

                @Override
                public void run() {
                    fairOrUnfair.doSomething(true);
                }
            }, "wulinfeng-" + i);
        }
        for (int i = 0; i < 20; i++) {
            threadArray[i].start();
        }
    }
}

  输出结果:

非公平锁开始:
wulinfeng-11获取锁定.
wulinfeng-2获取锁定.
wulinfeng-9获取锁定.
wulinfeng-6获取锁定.
wulinfeng-7获取锁定.
wulinfeng-10获取锁定.
wulinfeng-1获取锁定.
wulinfeng-0获取锁定.
wulinfeng-12获取锁定.
wulinfeng-8获取锁定.
wulinfeng-4获取锁定.
wulinfeng-3获取锁定.
wulinfeng-5获取锁定.
wulinfeng-16获取锁定.
wulinfeng-14获取锁定.
wulinfeng-13获取锁定.
wulinfeng-18获取锁定.
wulinfeng-15获取锁定.
wulinfeng-17获取锁定.
wulinfeng-19获取锁定.
公平锁开始:
wulinfeng-1获取锁定.
wulinfeng-0获取锁定.
wulinfeng-3获取锁定.
wulinfeng-5获取锁定.
wulinfeng-2获取锁定.
wulinfeng-4获取锁定.
wulinfeng-6获取锁定.
wulinfeng-8获取锁定.
wulinfeng-7获取锁定.
wulinfeng-14获取锁定.
wulinfeng-12获取锁定.
wulinfeng-10获取锁定.
wulinfeng-9获取锁定.
wulinfeng-16获取锁定.
wulinfeng-18获取锁定.
wulinfeng-11获取锁定.
wulinfeng-15获取锁定.
wulinfeng-13获取锁定.
wulinfeng-17获取锁定.
wulinfeng-19获取锁定.

  从上面结果看公平锁只能是大致按顺序来。

时间: 2024-10-25 10:56:55

锁的公平与非公平的相关文章

并发编程总结5-JUC-REENTRANTLOCK-3(非公平锁)

非公平锁和公平锁在获取锁的方法上,流程是一样的:区别主要表现在“尝试获取锁的机制不同”.“公平锁”在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待):而“非公平锁”在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁). 一.获取非公平锁 lock() NonfairSync类中实现 final void lock() { if (compareAndSetState(0, 1)) setExclusiveOwnerThre

公平锁与非公平锁

在Java的ReentrantLock构造函数中提供了两种锁:创建公平锁和非公平锁(默认).代码如下: public ReentrantLock(){ sync = new NonfairSync(); } public ReentrantLock(boolean fair) { sync = fair ? new FairSync() : new NonfairSync(); } 在公平的锁上,线程按照他们发出请求的顺序获取锁,但在非公平锁上,则允许'插队':当一个线程请求非公平锁时,如果在发

Java多线程编程4--Lock的使用--公平锁和非公平锁

公平与非公平锁:锁Lock分为"公平锁"和"非公平锁",公平锁表示线程获取锁的顺序是按照线程加锁的顺序来分配的,即先来先得的FIFO先进先出顺序.而非公平锁就是一种获取锁的抢占机制,是随机获得锁的,和公平锁不一样的就是先来的不一定先得到锁,这个方式可能造成某些线程一直拿不到锁,结果也就是不公平的了. 1.公平锁实例 public class Service { private ReentrantLock lock ; public Service(boolean i

多线程学习 公平锁和非公平锁

公平与非公平锁:锁lock分为 公平锁和非公平锁,公平锁表示现场获取锁的顺序是按照线程加锁的顺序来分配的, 即先来先得的FIFO先进先出顺序.而非公平锁就是一种获取锁的抢占机制,是随机获得的锁的,和公平锁不一样的就是先来 不一定先得到锁,这个方式可能造成某些线程一直拿不到锁. 首先来验证公平锁:创建service方法,使用lock进行锁定. public class Service { private ReentrantLock lock; public Service(boolean isFa

Java中的公平锁和非公平锁实现详解

在ReentrantLock中包含了公平锁和非公平锁两种锁,通过查看源码可以看到这两种锁都是继承自Sync,而Sync又继承自AbstractQueuedSynchronizer,而AbstractQueuedSynchronizer又继承自AbstractOwnableSynchronizer,下面是类的继承关系图: 其中AbstractOwnableSynchronizer是提供了设置占用当前锁的线程信息的方法,主要的锁的实现还是在AbstractQueuedSynchronizer中实现的

理解ReentrantLock的公平锁和非公平锁

学习AQS的时候,了解到AQS依赖于内部的FIFO同步队列来完成同步状态的管理,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个Node对象并将其加入到同步队列,同时会阻塞当前线程,当同步状态释放时,会把首节点中的线程唤醒,使其再次尝试获取同步状态. 这时,我有了一个疑问,AQS的同步队列是FIFO的,就是先来排队的先走.那怎么实现非公平锁呢?查阅了一些资料,总算知道了. 首先从公平锁开始看起. ReentrantLock 的公平锁 ReentrantLock 默认采用

最全Java锁详解:独享锁/共享锁+公平锁/非公平锁+乐观锁/悲观锁

在Java并发场景中,会涉及到各种各样的锁如公平锁,乐观锁,悲观锁等等,这篇文章介绍各种锁的分类: 公平锁/非公平锁 可重入锁 独享锁/共享锁 乐观锁/悲观锁 分段锁 自旋锁 01.乐观锁 vs 悲观锁 乐观锁与悲观锁是一种广义上的概念,体现了看待线程同步的不同角度,在Java和数据库中都有此概念对应的实际应用. 1.乐观锁 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号等机制. 乐观锁适用于多

Java多线程系列--“JUC锁”05之 非公平锁

获取非公平锁(基于JDK1.7.0_40) 非公平锁和公平锁在获取锁的方法上,流程是一样的:它们的区别主要表现在"尝试获取锁的机制不同".简单点说,"公平锁"在每次尝试获取锁时,都是采用公平策略(根据等待队列依次排序等待):而"非公平锁"在每次尝试获取锁时,都是采用的非公平策略(无视等待队列,直接尝试获取锁,如果锁是空闲的,即可获取状态,则获取锁).在前面的"Java多线程系列--"JUC锁"03之 公平锁(一)&q

AQS实现公平锁和非公平锁

基于AQS的锁(比如ReentrantLock)原理大体是这样:有一个state变量,初始值为0,假设当前线程为A,每当A获取一次锁,status++. 释放一次,status--.锁会记录当前持有的线程.当A线程拥有锁的时候,status>0. B线程尝试获取锁的时候会对这个status有一个CAS(0,1)的操作,尝试几次失败后就挂起线程,进入一个等待队列.如果A线程恰好释放,--status==0, A线程会去唤醒等待队列中第一个线程,即刚刚进入等待队列的B线程,B线程被唤醒之后回去检查这