深入Java多线程

10次阅读

共计 1728 个字符,预计需要花费 5 分钟才能阅读完成。

上篇文章已经对多线程有个初步的认识了,这次我们来看看 Java 的 Lock 锁,主要有以下知识点:

  • AQS

在学习 Lock 锁之前,我们先来看看什么是 AQS?

AQS

  • AQS 其实就是一个可以给我们实现锁的框架,juc 包中很多可阻塞的类比如ReentrantLock、ReadWriteLock 都是基于 AQS 构建的。
  • 内部实现的关键是:先进先出的队列、state 状态
  • 在 AQS 中实现了对等待队列的默认实现,子类只要重写部分的代码即可实现(大量用到了模板代码)
  • AQS 同时提供了 互斥模式(exclusive)和共享模式(shared)两种不同的同步逻辑。一般情况下,子类只需要根据需求实现其中一种模式,当然也有同时实现两种模式的同步类,如ReadWriteLock

state 状态

AQS 维护了一个 volatile int 类型的 state 变量,用来表示当前同步状态。
volatile 虽然不能保证操作的原子性,但是保证了当前变量 state 的可见性。

compareAndSetState

compareAndSetState()是原子操作,底层其实是调用系统的 CAS 算法,有关 CAS 可移步:CAS

protected final boolean compareAndSetState(int expect, int update) {return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
   }

Lock

Lock 是一个接口

public interface Lock {void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();}
  • lock()、tryLock()、tryLock(long time, TimeUnit unit)和 lockInterruptibly()是用来获取锁的。
  • unLock()方法是用来释放锁的。
  • newCondition()方法是创建一个条件对象,用来管理那些得到锁但是不能做有用工作的线程。

ReentrantLock

ReentrantLock,意思是 ”可重入锁“,线程可以重复地获得已经持有的锁。ReentrantLock 是唯一实现了 Lock 接口的类。接下来我们来看看有关源码:

静态内部类

ReentrantLock 实现了三个内部类,分别是Sync、NonfairSync 和 FairSync

abstract static class Sync extends AbstractQueuedSynchronizer
static final class NonfairSync extends Sync
static final class FairSync extends Sync

这些内部类都是 AQS 的子类,这就印证了我们之前所说的:AQS 是 ReentrantLock 的基础,AQS 是构建锁的框架.

构造器

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

默认实现的是非公平锁,传入 true 表示使用公平锁。

lock 方法

默认使用非公平锁的 lock 方法

 static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

     
        final void lock() {if (compareAndSetState(0, 1))// 尝试获取锁
                setExclusiveOwnerThread(Thread.currentThread());
            else  // 获取失败则调用 AQS 的 acquire 方法
                acquire(1);
        }

参考
Java3y 多线程
Java 技术之 AQS 详解

正文完
 0