Java多线程:JUC包-锁的封装

本文章是Java多线程系列的一篇文章,其他文章:
Java多线程:锁的底层实现
Java多线程:synchronized和volatile
Java多线程:JUC包-锁的封装
Java多线程:Thread的使用,以及wait(),notify(),notifyAll()
Java多线程:线程池

之前在Java多线程:synchronized和volatile这篇文章中,讲了Java在语言层面上对对多线程提供的支持。那么,在实际应用中,很多时候仅仅靠这两个关键字是不够的。那么如果我们想进行更骚的操作,就需要使用 java.util.concurrent 包下的工具类了。这个包的结构如下所示:

- java.util
    - atomic //原子操作
    - locks  //锁

本文将对其中的几个关键类进行介绍,其余的信息可以在参考链接里找到

一、接口&&抽象类

Lock接口

Lock 接口支持那些语义不同(重入、公平等)的锁规则。所谓语义不同,是指锁可是有“公平机制的锁”、”非公平机制的锁”、”可重入的锁”等等。

//@since 1.5
public interface Lock {

    //获取锁,失败则阻塞
    void lock();

    //Acquires the lock unless the current thread is interrupted.
    void lockInterruptibly() throws InterruptedException;

    //Acquires the lock only if it is free at the time of invocation.
    boolean tryLock();

    /**
     * @param time the maximum time to wait for the lock
     * @param unit the time unit of the time argument
     */
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

    void unlock();

    Condition newCondition();
}

ReadWriteLock接口

ReadWriteLock 接口以和Lock类似的方式定义了一些读取者可以共享而写入者独占的锁。JUC包只有一个类实现了该接口,即 ReentrantReadWriteLock,因为它适用于大部分的标准用法上下文。但程序员可以创建自己的、适用于非标准要求的实现。

public interface ReadWriteLock {
    //Returns the lock used for reading.
    Lock readLock();
    //Returns the lock used for writing.
    Lock writeLock();
}

Condition接口

Condition需要和Lock联合使用,它的作用是代替Object监视器方法,可以通过await(),signal()来休眠/唤醒线程。Condition 接口描述了可能会与锁有关联的条件变量。这些变量在用法上与使用 Object.wait 访问的隐式监视器类似,但提供了更强大的功能。需要特别指出的是,单个 Lock 可能与多个 Condition 对象关联。为了避免兼容性问题,Condition 方法的名称与对应的 Object 版本中的不同。

//nano 指的是nanoSecond,纳秒
//@since 1.5
public interface Condition {

    //Causes the current thread to wait until it is signalled or interrupted.
    void await() throws InterruptedException;

    //Causes the current thread to wait until it is signalled.
    void awaitUninterruptibly();

    //Causes the current thread to wait until it is signalled or interrupted,or the specified waiting time elapses.
    long awaitNanos(long nanosTimeout) throws InterruptedException;

    //Causes the current thread to wait until it is signalled or interrupted,or the specified deadline elapses.
    boolean awaitUntil(Date deadline) throws InterruptedException;

    //Wakes up one waiting thread.
    void signal();

    //Wakes up all waiting threads.
    void signalAll();
}

AbstractQueuedSynchronizer抽象类

AbstractQueuedSynchronizer就是被称之为AQS的类,它是一个非常有用的超类,可用来定义锁以及依赖于排队阻塞线程的其他同步器;

ReentrantLock,ReentrantReadWriteLock,CountDownLatch,CyclicBarrier和Semaphore等这些类都是基于AQS类实现的。AbstractQueuedLongSynchronizer 类提供相同的功能但扩展了对同步状态的 64 位的支持。两者都扩展了类 AbstractOwnableSynchronizer(一个帮助记录当前保持独占同步的线程的简单类)。

LockSupport类

LockSupport的功能和”Thread中的Thread.suspend()和Thread.resume()有点类似”,LockSupport中的park() 和 unpark() 的作用分别是阻塞线程和解除阻塞线程。但是park()和unpark()不会遇到“Thread.suspend 和 Thread.resume所可能引发的死锁”问题。

二、具体实现类

重入锁:ReentrantLock

ReentrantLock是独占锁。所谓独占锁,是指只能被独自占领,即同一个时间点只能被一个线程锁获取到的锁(互斥同步语义)。ReentrantLock锁包括”公平的ReentrantLock”和”非公平的ReentrantLock”(公平非公平语义)。”公平的ReentrantLock”是指”不同线程获取锁的机制是公平的”,而”非公平的  ReentrantLock”则是指”不同线程获取锁的机制是非公平的”,ReentrantLock是”可重入的锁”(可重入语义)。

重入读写锁:ReentrantReadWriteLock

ReentrantReadWriteLock允许多个读线程同时访问,但不允许写线程和读线程、写线程和写线程同时访问。相对于排他锁,提高了并发性。在实际应用中,大部分情况下对共享数据(如缓存)的访问都是读操作远多于写操作,这时ReentrantReadWriteLock能够提供比排他锁更好的并发性和吞吐量。

AtomicLong、 LongAdder

AtomicLong是通过对单一变量进行CAS操作来实现原子操作的。那么在高并发的场景下,就会导致大量进程CAS操作失败,造成资源的浪费。而LongAdder另辟蹊径,将单一value的CAS操作分担到多个value中去,降低单个value的 ‘热度‘ ,这样就能有效减少CAS失败带来的开销。

LongAdder的作者是Doug Lea ,Doug Lea对该类的说明是: 低并发时LongAdder和AtomicLong性能差不多,高并发时LongAdder更高效。实际的测试也证明了这一点。关于LongAdder的分析网上已经有很详细的文章,这里就不再赘述,可以参考如下的文章:

源码分析:LongAdder 空间换时间-比AtomicLong还高效的无锁实现
Benchmark:LongAdder vs AtomicLong

参考:
JUC包中的锁-系列文章
ReentrantLock和synchronized的区别

你可能感兴趣的:(编程语言:Java,虚拟机:JVM,Java多线程)