【juc学习之路第7天】ReentrantLock与ReentrantReadWriteLock

6 篇文章 0 订阅

线程锁

位于jucl下,主要有两种接口类型的锁:

  • Lock:

    包含以下三种形式锁的类型:

    • 公平锁:不同的线程获取锁的过程是公平的
    • 非公平锁:不同线程获取锁的过程是不公平的,允许竞争获取。
    • 可重入锁:同一个锁可以被一个线程多次获取,避免死锁的出现。
  • ReadWhiteLock: 提供读锁和写锁,读时锁共享,修改时独占。

ReentrantLock

互斥锁或叫独占锁,是Lock的实现类,意思是一旦获取到锁之后,其他线程不允许再操作(无论读或写),synchronized也是互斥锁。

先观察下ReentrantLock的源码:

package java.util.concurrent.locks;
public class ReentrantLock implements Lock, java.io.Serializable {
    private static final long serialVersionUID = 7373984872572414699L;
    /** Synchronizer providing all implementation mechanics */
    private final Sync sync;
}

继续扒Sync

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = -5179523762034025860L;
    }

继续扒AbstractQueuedSynchronizer:(这就是JUC另一核心:AQS

public abstract class AbstractQueuedSynchronizer
		extends AbstractOwnableSynchronizer implements java.io.Serializable {
    private static final long serialVersionUID = 7373984972572414691L;
}

继续:

public abstract class AbstractOwnableSynchronizer
    implements java.io.Serializable {

AQS 的本质是一个执行队列,所有的待执行线程全部都保存在一个队列之中,可以解决死锁问题。在JUC的AQS里面提供了一个CLH队列。CLH (Craig, Landin, and Hagersten)锁是一种基于链表的可扩展、高性能、公平的自旋锁,申请线程只在本地变量上自旋,它不断轮询前驱的状态,如果发现前驱释放了锁就结束自旋。

/** JDK17 CLH Nodes */
abstract static class Node {
		volatile Node prev;       // initially attached via casTail
		volatile Node next;       // visibly nonnull when signallable
		Thread waiter;            // visibly nonnull when enqueued
		volatile int status;      // written by owner, atomic bit ops by others
}

获取锁的机制是由AQS的子类Sync提供的,Sync也有两个子类:FairSyncNonfairSync

ReentrantLock在操作时有两种情况:

  • 多个线程抢占互斥锁资源:

在这里插入图片描述

  • 互斥锁抢夺与等待:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fShBnwgJ-1653492592222)(/Users/alexanderron/Library/Application Support/typora-user-images/image-20220525103823265.png)]

等待的线程会保存在AQS中的CLH等待队列中,锁竞争的机制是由Sync决定的,可以发现默认是非公平锁,是比公平锁更有效率,也是更能发挥出计算机的性能。

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

lock()与unlock()

public void lock() { // 此处简写
		sync.acquire(1);
}
public void unlock() {
  	sync.release(1);
}

在进行线程锁定的过程之中是依靠线程锁定数量的控制,锁定的时候都会调用锁定计数的方法:acquire(1),每当一个线程被锁定了,这个时候计数就会+1,CLH就依靠锁定数量是否为0来确定是否有锁定的线程,从而解决线程死锁问题。在每一次解锁的时候会调用release(1)释放一个锁定。

设计一个抢票系统

class SaleSystem {
    private int ticket;
    private static final ReentrantLock LOCK = new ReentrantLock();
    public SaleSystem() {}
    public SaleSystem(int ticket) {
        this.ticket = ticket;
    }
    public void sale() {
        LOCK.lock();
        try {
            if (this.ticket > 0) {
                System.out.printf("【%s】售票成功!剩余票数:%d\n",
                        Thread.currentThread().getName(), this.ticket --);
            } else {
                System.out.printf("【%s】没有票啦!\n", Thread.currentThread().getName());
            }
        }catch (Exception e) {
            e.printStackTrace();
        } finally {
            LOCK.unlock();
        }
    }
}
public class Main {
    public static void main(String[] args) throws Exception {
        SaleSystem saleSystem = new SaleSystem(5);
        for (int i = 0; i < 8; i ++) {
            new Thread(saleSystem::sale).start();
        }
    }
}

【Thread-0】售票成功!剩余票数:5
【Thread-4】售票成功!剩余票数:4
【Thread-1】售票成功!剩余票数:3
【Thread-3】售票成功!剩余票数:2
【Thread-5】售票成功!剩余票数:1
【Thread-2】没有票啦!
【Thread-6】没有票啦!
【Thread-7】没有票啦!

ReentrantReadWriteLock

非独占锁,读锁属于共享锁,所有的读线程共享一个共享锁,当写锁工作的时候,整个共享锁将进入到暂停阶段,等待写完成后再进行多个并发并发读取。

img

在这里插入图片描述

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    private final ReentrantReadWriteLock.ReadLock readerLock;
    private final ReentrantReadWriteLock.WriteLock writerLock;
    final Sync sync;
    public ReentrantReadWriteLock() {
        this(false);
    }
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }
    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

edanhuang

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

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

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

打赏作者

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

抵扣说明:

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

余额充值