并发包中的管程

并发包中的管程

什么是Lock和Condition

并发包SDK中存在管程的另一个实现即Lock和Condition,其中Lock可以解决互斥问题,Condition可以解决管程的同步问题(通信和协作)。

读到这里有人肯定马上就有疑问了,用synchronized实现的管程完全可以解决互斥和同步问题,为什么要在SDK中重新实现一种管程方式呢?这不是多此一举吗?

没错synchronized确实可以解决互斥和同步问题,但是存在即合理,回答这个问题之前先回顾下死锁问题成立的四大条件。

  • 互斥(同一时间资源只有一个线程访问)
  • 持有且等待(持有资源A时尝试获取资源B,尝试等待的时间段内不释放资源A)
  • 不可抢占(持有的资源不能被其它线程抢占)
  • 循环等待(持有资源A获取资源B时会一直等待)

而解决死锁问题就是破坏其中一个条件即可,但是互斥条件不能破坏,因为破坏失去加锁的意义,所以可破坏其它三个死锁条件。

  • 破坏持有且等待:一次性申请所有的资源。
  • 破坏循环等待:将资源排序,让资源有序就可以破坏死活。
  • 破坏不可抢占:想要破坏不可抢占条件需要主动释放资源,但如果加锁的是synchronized那么获取不到资源就会进入睡眠,不会响应其它资源。

显然破坏不可抢占条件,synchronized锁并不能做到,所以只能另外设计一把互斥锁来解决死锁问题,那么互斥锁应该存在哪些优点呢?

  • 能够响应中断:synchronized不能破坏不可抢占条件的原因也就是因为线程获取资源A后,去竞争资源B一旦失败就会进入睡眠状态,这时发生死锁将没有机会唤醒线程,如果阻塞的线程能够响应中断,唤醒线程那么就有机会去释放资源A,达到破坏不可抢占的条件。
  • 非阻塞地获取锁:当获取资源失败后,不进入阻塞状态而是立马将结果返回,那么线程就能有机会去释放持有的资源。
  • 支持超时:如果线程在一定时间内没有获取到锁,并不是进入阻塞状态而是返回错误,这样也能有机会释放持有的资源。

Lock如何保证互斥性

显然Lock做到了这一点,能够同时满足上诉的三个优点

  • 支持中断的API

    void lockInterruptibly() throws InterruptedException;
    
  • 支持超时API

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    
  • 非阻塞获取锁API

    boolean tryLock();
    

Lock如何保证可见性

Lock锁的可见性不能直接应用于JMM(JAVA内存模型)的Happens-Before规则,其锁的可见性规则只是针对synchronized,那么对于上面的代码value能不能保证可见性呢?

class Test {
  private final Lock rtl =
  new ReentrantLock();
  int value;
  public void addOne() {
    rtl.lock();  
    try {
      value+=1;
    } finally {
      rtl.unlock();
    }
  }
}

答案是肯定的,Lock接口的具体实现ReentrantLock中存在一个被volatile修饰的state变量,简称状态机模式。

private volatile int state;

protected final int getState() {
    return state;
}

加锁部分源码

// acquires传进来是1
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;
}

解锁部分源码

// releases传进来是1
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;
}

简化版本如下

class SampleLock {
  volatile int state;
  // 加锁
  lock() {
    // 读取state变量
    // 省略代码无数
    state = 1;
  }
  // 解锁
  unlock() {
    // 读取state变量
    // 省略代码无数
    state = 0;
  }
}

综上可以简单推出

  • 根据顺序性规则(单线程下前面的操作Happens-Before后面的操作)线程T1 value+=1;Happens-Before于rtl.unlock();

  • 根据valatile规则(对volatile变量的写操作Happens-Before变量的读操作)线程T1 unlock();Happens-Before于线程T2 lock();

  • 根据传递性规则(A Happens-Before B,B Happens-Before C,那么A Happens-Before C)得到线程T1 value+=1 Happens-Before 线程T2 lock操作.

所以线程T1对value进行操作,对于线程T2是可见的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值