Lock&Condition

锁类继承层次

Lock
void lock()
void lockInterruptibly()
boolean tryLock()
boolean tryLock(long time, TimeUnit unit)
void unlock()
Condition newCondition()
ReentrantLock
AbstractOwnableSynchronizer
private transient Thread exclusiveOwnerThread
AbstractQueuedSynchronizer
private volatile int state
private transient volatile Node head
private transient volatile Node tail
private Node addWaiter(Node mode)
final boolean acquireQueued(final Node node, int arg)
private final boolean parkAndCheckInterrupt()
public final boolean release(int arg)
NonFairSync
public final void acquire(int arg)
protected final boolean tryAcquire(int acquires)
protected final boolean tryRelease(int releases)
FairSync
public final void acquire(int arg)
protected final boolean tryAcquire(int acquires)
protected final boolean tryRelease(int releases)
Sync
Node
volatile Node prev
volatile Node next
volatile Thread thread

锁工具类LockSupport

public class LockSupport {

    private static final Unsafe UNSAFE;
    
    public static void park() {
        UNSAFE.park(false, 0L);
    }
    
    public static void unpark(Thread var0) {
        if (var0 != null) {
            UNSAFE.unpark(var0);
        }

    }
}

线程中调用park()会阻塞当前线程,在其它线程中调用unpark(thread),可精准唤醒指定的线程

参数作用

AbstractOwnableSynchronizer

Thread exclusiveOwnerThread : 表示当前持有锁的线程

AbstractQueuedSynchronizer

volatile int state :当前锁的状态,cas保证线程安全
transient volatile Node head:阻塞队列头
transient volatile Node tail:阻塞队列尾
Node addWaiter(Node mode):将当前线程构建成node加入阻塞队列
boolean acquireQueued(final Node node, int arg):实现线程阻塞逻辑调用
boolean parkAndCheckInterrupt():线程的自我阻塞
boolean release(int arg) :解锁

NonFairSync&FairSync

void acquire(int arg):获取锁
final boolean tryAcquire(int acquires):尝试获取锁

boolean tryRelease(int releases) :尝试解锁

加锁流程图

true
false
ReentrantLock.lock
Sync.acquire
Sync.tryAcquire
return
AbstractQueuedSynchronizer.addWaiter
AbstractQueuedSynchronizer.parkAndCheckInterrupt
acquire(int arg) 与 tryAcquire(int arg)
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
}

当 acquireQueued(…)返回 true 时,会调用 selfInterrupt(),自己给自己发送中断信号,也就是自
己把自己的中断标志位设为true。之所以要这么做,是因为自己在阻塞期间,收到其他线程中断信号没
有及时响应,现在要进行补偿。这样一来,如果该线程在lock代码块内部有调用sleep()之类的阻塞方
法,就可以抛出异常,响应该中断信号。

tryAcquire(int arg)

非公平锁实现

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
        
        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) // overflow
                    throw new Error("Maximum lock count exceeded");
                setState(nextc);
                return true;
            }
            return false;
        }        

公平锁实现

    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;
        }

由代码可以看出,公平锁获取锁前会判断(hasQueuedPredecessors())队列中是否有任务线程,没有才会去竞争锁状态(state)。而非公平锁则是直接去竞争锁状态(state)

阻塞逻辑acquireQueued(final Node node, int arg)
    final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                //、判断是不是头节点,是的话回去尝试竞争锁状态
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    //竞争到锁以后直接返回
                    return interrupted;
                }
                //没有则进入线程阻塞parkAndCheckInterrupt()
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

进入队列中,都是公平锁了。会进行如下操作
1、判断是不是头节点,是的话回去尝试竞争锁状态
2、竞争到锁以后直接返回
3、没有则进入线程阻塞parkAndCheckInterrupt()
4、阻塞线程唤醒后,重复1234操作

parkAndCheckInterrupt()
    private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);
        return Thread.interrupted();
    }

解锁流程图

true
false
ReentrantLock.unlock
Sync.release
Sync.tryRelease
LockSupport.unpark
return false
return true
release(int arg)
    public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
            //唤醒阻塞的头节点线程
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease(int releases)
        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;
        }

修改状态时没有用到cas,因为此时处理是单线程的。状态等于0时才清空持锁线程,为了处理锁重入的情况。

unparkSuccessor(Node node)
    private void unparkSuccessor(Node node) {
       
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

可中断锁的实现lockInterruptibly()

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly(1);
    }
    
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())//中断标识检测
            throw new InterruptedException();
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }
    
    private void doAcquireInterruptibly(int arg)
        throws InterruptedException {
        final Node node = addWaiter(Node.EXCLUSIVE);
        boolean failed = true;
        try {
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    //关键点
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }
    
    

主要关键点在doAcquireInterruptibly方法中线程阻塞后判断中断标识,如果标识为true,则抛出InterruptedException。以及acquireInterruptibly方法中获取锁前的中断标识检测。

读写锁ReentrantReadWriteLock

实现原理,重写并拆分了AbstractQueuedSynchronizer中的state状态,高16位存储读锁的状态,低16位存储写锁的状态。
对于写锁来说,只需要判断state>0则说明有线程占有锁,就需要进入阻塞队列,而对于读锁来说,先判断state,等于0说明无线程占用,>0再判断是否写线程占用(低16位),写占用则进入阻塞队列,读占用则获取到锁。
写线程占有较高优先级,因为不这样做的话,可能多个读线程一直执行,导致写线程饥饿。

Condition

final ReentrantLock lock = new ReentrantLock(); 
private final Condition notEmpty = lock.newCondition(); 
private final Condition notFull =  Lock.newCondition();

notEmpty.await();//阻塞
notEmpty.signal();//唤醒

Condition实现原理

所有的condition都是借助ConditionObject对象来实现。对象里面维护一个双向阻塞线程链表,通过这个链表来进行统一的唤醒操作。

StampedLock锁

jdk1.8以后提供,相比较于ReentrantWriteLock。读写默认是不互斥的。只有当读写冲突时,才会升级为互斥。数据读出来后会有个版本号,再使用之前会用版本号校验是否被改过,若被改过才会去竞争锁,重新读数据。

StampedLock stampLock = new StampedLock();
long stamp=stampLock.writeLock();//上写锁
stampLock.unWriteLock(stamp);//解锁

long rStamp=stampLock.tryOptimisticRead();//乐观读
if(!stampLock.validate(rStamp)){
    stampLock.readLock();//加读锁
    stampLock.unReadLock(rStamp);//解读锁
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
### 回答1: blockcondition是多线程编程中常用的场景。block表示阻止线程的执行,直到某个条件被满足。而condition则是在多个线程之间协调的工具,它允许一个线程等待另一个线程发出信号,以便在特定的条件下继续执行。在复杂的多线程编程场景中,blockcondition可以帮助我们避免死锁和竞态条件等问题,保证程序的正确性和性能。 ### 回答2: lockcondition是在多线程编程中常用的同步机制。 lock(锁)是一种互斥的机制,用于保护共享资源。当一个线程获取到锁后,其他线程将无法访问被锁住的资源,直到该线程释放了锁。这样可以确保在多个线程同时访问共享资源时,不会发生数据竞争和不一致的问题。因此,在并发编程中,当多个线程需要同时访问共享资源时,需要使用lock来进行同步,以避免数据冲突。 condition(条件)是一种线程间的通信机制,用于实现线程的协调与通信。在某些情况下,我们需要一个线程等待另一个线程满足某些条件后再继续执行。condition提供了一个等待和唤醒的机制,使得线程可以在满足条件之前等待,而不是忙等待。通过condition,我们可以让一个线程等待某个条件成立,而另一个线程在满足条件时进行通知通知等待的线程继续执行。这样可以提高线程的效率和协调性。 综上所述,lock主要用于保护共享资源,防止数据冲突和不一致,而condition主要用于线程间的协调与通信,实现线程的等待和唤醒操作。在并发编程中,我们使用lockcondition能够更好地控制多个线程的执行顺序和并发访问共享资源的情况,提高程序的性能和正确性。 ### 回答3: Lock(锁)和Condition(条件)都是多线程编程中常用的工具,用于实现线程之间的同步。 Lock是一个互斥锁,可以协调线程对共享资源的访问。常用方法有:acquire(获取锁)和release(释放锁)。使用Lock可以确保在同一时间内,只有一个线程可以访问共享资源,避免了线程之间的冲突。 Lock的使用场景包括但不限于以下几种情况: 1. 多个线程需要同时访问临界资源,需要确保在同一时间只有一个线程能够访问,其他线程需要等待。 2. 保护共享数据的完整性,避免多线程同时修改共享数据导致数据不一致。 3. 防止线程之间的竞态条件,即多线程执行顺序不确定导致结果异常。 Condition是用于线程间的协调与通信的工具,需要和Lock一起使用。它可以确保在特定条件下才执行线程的某些操作。常用方法有:await(等待),signal(发送信号)和signalAll(发送信号给所有线程)。 Condition的使用场景包括但不限于以下几种情况: 1. 当一个线程需要等待另一个线程的某个操作完成后才能继续执行时,可以使用Condition进行等待和唤醒操作。 2. 多个线程之间需要按照特定的顺序执行,可以利用Condition的signal和await方法来实现线程按序执行。 3. 多个线程需要根据某个条件进行相互通信,可以使用Condition进行信号的发送和接收。 综上所述,LockCondition是用来实现线程之间的同步的工具,Lock用于保护共享资源的访问,而Condition用于线程间的通信与协作。它们的使用场景不同,但常常结合使用来解决多线程编程中的同步问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搬砖工-->攻城狮

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值