Java多线程编程-ReentrantLock(2)

公平锁和非公平锁

我们在使用java多线程编程的时候,经常会用到锁机制,锁分为公平锁和非公平锁。
公平锁:表示线程获取锁的顺序是按照线程加锁的顺序来进行分配的,先来先得先进先出的顺序进行的。
非公平锁:就是一种锁的抢占机制,是随机获取锁的,这样会有的线程一直获取不到锁,因此对于该线程来说是不公平的。

使用ReentrantLock实现公平锁

我们使用ReentrantLock时候,ReentrantLock的构造函数可以传入一个boolean类型的参数,根据不同的值返回不同的实现方式,源码如下:

public ReentrantLock() {
    sync = new NonfairSync(); 
}

public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
}

由上面的代码可以看出,默认是进行不公平的锁机制,使用new NonfairSync()进行,公平锁为new FairSync()。因此创建ReentrantLock(true)使用公平锁策略,ReentrantLock(false)的时候使用非公平锁机制,因此 Java多线程编程-ReentrantLock(1)都是使用的默认的非公平锁策略。

公平锁

public class ReentrantLockObj {
    private ReentrantLock reentrantLock;
    public ReentrantLockObj(boolean isFair) {
        reentrantLock = new ReentrantLock(isFair);
    }
    public void fun() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name :" + Thread.currentThread().getName() + " get lock");
        } finally {
            reentrantLock.unlock();
        }
    }
}

运行:

public class ReentrantLockObjMain {

    public static void main(String[] args) {
        ReentrantLockObj reentrantLockObj = new ReentrantLockObj(true);
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("current thread name:" + Thread.currentThread().getName() + " do running");
                reentrantLockObj.fun();
            }
        };
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            threadList.add(new Thread(runnable, "thread" + i));
        }
        for (Thread thread : threadList) {
            thread.start();
        }
    }
}

运行结果:
在这里插入图片描述
我们上面创建了10个线程,每个线程依次运行,说明我们使用ReentrantLock(true)创建的是公平锁。
我们可以将上面的true变为false,我们看看运行结果:

ReentrantLockObj reentrantLockObj = new ReentrantLockObj(false);

运行结果:
在这里插入图片描述
从上面的可以看出,线程的执行和获取锁的事混乱的。说明使用ReentrantLock(false)创建的非公平锁的使用。

ReentrantLock相关API

我们在使用ReentrantLock类进行锁操作的时候,只是用到了lock和unlock方法,我们看看源码:
lock():获取锁,如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1.如果当前线程已经持有该锁,则持有计数将增加一,并且该方法将立即返回。如果该锁由另一个线程持有,则出于线程调度目的,当前线程将被禁用,并且在获取该锁之前,该线程处于休眠状态,这时将锁持有计数设置为1。

public void lock() {
    sync.lock();
}

sync对象是一个Sync接口,实现了Lock,根据是否为公平锁调用不同的实现,非公平锁如下:

 final void lock() {
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread());
    else
        acquire(1);
}

公平锁如下:

 final void lock() {
    acquire(1);
}

ReentrantLock.getHoldCount()

getHoldCount():查询当前线程对该锁的保持次数(调用lock方法的次数)。对于每个未与解锁动作匹配的锁定动作,线程都会拥有一个锁。保留计数信息通常仅用于测试和调试目的.
源码:

public int getHoldCount() {
    return sync.getHoldCount();
}
//sync调用的方法
final int getHoldCount() {
    return isHeldExclusively() ? getState() : 0;
}

验证:

public class ReentrantLockAPIMain1 {
    private static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) {
        testFun1();
    }
    public static void testFun1() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
            testFun2();
        } finally {
            reentrantLock.unlock();
        }
    }
    public static void testFun2() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
        } finally {
            reentrantLock.unlock();
        }
    }
}

运行结果:
在这里插入图片描述
上面是第一行是testFun1方法调用的,第二行是方法testFun2打印的,调用两次lock方法

ReentrantLock.getQueueLength()

getQueueLength():返回等待获取此锁的线程数的估计值。该值只是一个估计值,因为在此方法遍历内部数据结构时,线程数可能会动态变化。此方法设计用于监视系统状态,而不用于同步控制。
源码:

public final int getQueueLength() {
    return sync.getQueueLength();
}
//sync调用的方法 AbstractQueuedSynchronizer.Node
      /**
      * 要加入CLH锁,您可以自动将其作为新尾部进行拼接。要出队,您只需设置头字段。
      +------+  prev +-----+       +-----+
       head |      | <---- |     | <---- |     |  tail
            +------+       +-----+       +-----+ **/
public final int getQueueLength() {
    int n = 0;
    for (Node p = tail; p != null; p = p.prev) {
        if (p.thread != null)
            ++n;
    }
    return n;
}

验证:

public class ReentrantLockAPIMain2 {

    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testFun1();
            }
        };
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            threadList.add(new Thread(runnable, "thread" + i));
        }
        threadList.stream().forEach(thread -> {
            thread.start();
        });
        Thread.sleep(5000);
        System.out.println("wait lock number:" + reentrantLock.getQueueLength());
    }

    public static void testFun1() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

}

运行结果:
在这里插入图片描述
我们创建了10个线程,在一个线程中进行等待,剩余的9个线程正在等待获取锁,程序还没有结束。

ReentrantLock.getWaitQueueLength(Condition condition)

getWaitQueueLength(Condition condition):返回在与此锁关联的给定条件下等待的线程数的估计值。请注意,由于超时和中断可能随时发生,因此估算值仅用作实际侍者数的上限。此方法设计用于监视系统状态,而不用于同步控制。
源码:

public int getWaitQueueLength(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return 	sync.getWaitQueueLength((AbstractQueuedSynchronizer.ConditionObject)condition);
}
//sync调用的方法
public final int getWaitQueueLength(ConditionObject condition) {
        if (!owns(condition))
            throw new IllegalArgumentException("Not owner");
        return condition.getWaitQueueLength();
    }

验证:

public class ReentrantLockAPIMain3 {

    private static ReentrantLock reentrantLock = new ReentrantLock();
    private static Condition condition = reentrantLock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testFun1();
            }
        };
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            threadList.add(new Thread(runnable, "thread" + i));
        }
        threadList.stream().forEach(thread -> {
            thread.start();
        });
        Thread.sleep(5000);
        testFun2();
    }

    public static void testFun1() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void testFun2() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " reentrantLock.getWaitQueueLength(condition) number:"
                    + reentrantLock.getWaitQueueLength(condition));
            condition.signal();
        } finally {
            reentrantLock.unlock();
        }
    }
}

运行结果:
在这里插入图片描述
这里使用同一个condition,所以获取的个数为10,如果创建多个condition的,和这个差不多,是针对不同condition进行统计。我们从上面的源码可以看出,如果不传condition的时候将抛出空指针异常

ReentrantLock.isFair()

isFair():返回是否为公平锁
源码:

public final boolean isFair() {
    return sync instanceof FairSync;
}

有创建的源码可以看出,公平和非公平锁是根据sync 的不同实现来获得的。因此判断sync的实现是否为公平实现还是非公平实现即可。

ReentrantLock.hasQueuedThread(Thread thread)

hasQueuedThread(Thread thread):查询给定线程是否正在等待获取此锁。请注意,由于取消可能随时发生,因此返回{@code true}并不能保证该线程将获得此锁。此方法主要设计用于监视系统状态。
源码:

public final boolean hasQueuedThread(Thread thread) {
    return sync.isQueued(thread);
}
//调用sync方法 AbstractQueuedSynchronizer.hasQueuedThreads
public final boolean isQueued(Thread thread) {
    if (thread == null)
        throw new NullPointerException();
    for (Node p = tail; p != null; p = p.prev)
        if (p.thread == thread)
            return true;
    return false;
}

验证:

public class ReentrantLockAPIMain4 {

    private static ReentrantLock reentrantLock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testFun1();
            }
        };
        Thread thread = new Thread(runnable, "thread01");
        thread.start();
        Thread.sleep(5000);
        Thread thread1 = new Thread(runnable, "thread02");
        thread1.start();
        Thread.sleep(5000);
        System.out.println(reentrantLock.hasQueuedThread(thread));
        System.out.println(reentrantLock.hasQueuedThread(thread1));
    }

    public static void testFun1() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " call lock number:" + reentrantLock.getHoldCount());
            Thread.sleep(Integer.MAX_VALUE);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

}

运行结果:
在这里插入图片描述
从运行可以看出,线程1正在等待。

ReentrantLock.hasQueuedThreads()

hasQueuedThreads():查询是否有任何线程正在等待获取此锁。请注意,由于取消可能随时发生,因此返回true并不能保证任何其他线程都会获得此锁。此方法主要设计用于监视系统状态
这个方法和上面的方法功能基本上差不多,一个传入thread对象一个是针对所有的线程。

ReentrantLock.hasWaiters(Condition condition)

hasWaiters(Condition condition):查询是否有任何线程正在等待与此锁关联的给定条件。请注意,由于超时和中断随时可能发生,因此返回 true并不能保证将来的signal会唤醒任何线程。此方法主要设计用于监视系统状态。
源码:

public boolean hasWaiters(Condition condition) {
    if (condition == null)
        throw new NullPointerException();
    if (!(condition instanceof AbstractQueuedSynchronizer.ConditionObject))
        throw new IllegalArgumentException("not owner");
    return sync.hasWaiters((AbstractQueuedSynchronizer.ConditionObject)condition);
}
//sync调用的方法 AbstractQueuedSynchronizer.hasWaiters
public final boolean hasWaiters(ConditionObject condition) {
     if (!owns(condition))
         throw new IllegalArgumentException("Not owner");
     return condition.hasWaiters();
 }

验证:

public class ReentrantLockAPIMain5 {

    public static ReentrantLock reentrantLock = new ReentrantLock();

    public static Condition condition = reentrantLock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testFun1();
            }
        };
        List<Thread> threadList = new ArrayList<>();
        for (int i = 0; i < 10; i++) {
            threadList.add(new Thread(runnable, "thread0" + i));
        }
        threadList.stream().forEach(thread -> thread.start());
        Thread.sleep(5000);
        testFun2();
    }

    public static void testFun1() {
        reentrantLock.lock();
        try {
            condition.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            reentrantLock.unlock();
        }
    }

    public static void testFun2() {
        reentrantLock.lock();
        try {
            System.out.println("has wait condition? " + reentrantLock.hasWaiters(condition) + " thread numbers is " + reentrantLock.getWaitQueueLength(condition));
            condition.signal();
        } finally {
            reentrantLock.unlock();
        }
    }
}

运行结果:
在这里插入图片描述
等待同一个condition条件的线程正在等待中的数为10,返回为true

ReentrantLock.isLocked()

isLocked():查询此锁定是否由任意线程保持
源码:

final boolean isLocked() {
    return getState() != 0;
}

ReentrantLock.isHeldByCurrentThread()

isHeldByCurrentThread():查询此锁是否由当前线程持有。类似于内置监视器锁的ThreadholdsLock(Object)方法,该方法通常用于调试和测试。
源码:

public boolean isHeldByCurrentThread() {
    return sync.isHeldExclusively();
}
//sync调用方法
protected final boolean isHeldExclusively() {
    // While we must in general read state before owner,
    // we don't need to do so to check if current thread is owner
    return getExclusiveOwnerThread() == Thread.currentThread();
}

ReentrantLock.lockInterruptibly()

lockInterruptibly():除非当前线程为Threadinterrupt interrupted,否则获取锁。如果没有其他线程持有该锁,则获取该锁并立即返回,将锁保持计数设置为1.如果当前线程已经持有此锁,则持有计数将增加一,并且该方法将立即返回。如果该锁由另一个线程持有,则出于线程调度的目的,当前线程将被禁用,并处于休眠状态,直到发生以下两种情况之一:
1.该锁由当前线程获取
2.当前线程还有其他一些线程Threadinterrupt interrupts

源码:

public void lockInterruptibly() throws InterruptedException {
    sync.acquireInterruptibly(1);
}
//sync调用的方法 AbstractQueuedSynchronizer.acquireInterruptibly
public final void acquireInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (!tryAcquire(arg))
        doAcquireInterruptibly(arg);
}

验证:

public class ReentrantLockAPIMain6 {
    public static ReentrantLock reentrantLock = new ReentrantLock();
    public static void main(String[] args) throws InterruptedException {
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                testFun1();
            }
        };
        Thread thread = new Thread(runnable, "thread01");
        thread.start();
        Thread.sleep(1000);
        Thread thread1 = new Thread(runnable, "thread02");
        thread1.start();
        thread1.interrupt();
    }

    public static void testFun1() {
        reentrantLock.lock();
        try {
            System.out.println("current thread name:" + Thread.currentThread().getName() + " get lock");
            for (int i = 0; i < 6000; i++) {
                Math.random();
            }
            System.out.println("get lock end " + Thread.currentThread().getName());
        } finally {
            if (reentrantLock.isHeldByCurrentThread()) {
                reentrantLock.unlock();
            }
        }
    }
}

运行结果:
在这里插入图片描述
可以运行成功,我们使用lockInterruptibly方法试试:我们将lock方法变为lockInterruptibly方法

 reentrantLock.lockInterruptibly();

运行会出现报错信息:
在这里插入图片描述

ReentrantLock.tryLock()和ReentrantLock.tryLock(long time,TimeUnit unit)

tryLock():仅在调用时另一个线程未持有该锁时才获取该锁。如果没有其他线程持有该锁,则获取该锁,并立即返回值true,并将锁保持计数设置为1。即使已将此锁设置为使用公平的排序策略,如果可用,则对tryLock()的调用将立即获取该锁,无论当前是否有其他线程在等待该锁。锁。即使破坏公平性,这种“讨价还价”的行为在某些情况下还是有用的。如果要遵守此锁的公平性设置,请使用几乎等效的tryLock(long,TimeUnit)方法,tryLock(0,TimeUnit.SECONDS)(它还会检测到中断)。如果当前线程已经持有此锁,则持有计数将增加1,并且该方法返回true.如果锁由另一个线程持有,则此方法将立即返回值false

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值