zoukankan      html  css  js  c++  java
  • Java 多线程

    总结

    1. 什么是AQS ?

    AQS的全称是AbstractQueuedSynchronizer,它是为Java中几乎所有的锁和同步器提供一个基础框架, 拥有一个同步队列和多个等待队列:

    1.1 AQS vs 锁

    锁和同步器很好地隔离了使用者和实现者 所需关注的领域:

    • 锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线 程并行访问),隐藏了实现细节;
    • 同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、 线程的排队、等待与唤醒等底层操作。锁和同步器很好地隔离了使用者和实现者 所需关注的领域。

    如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的工作线程,并且将共享资源设置为锁定状态。

    如果被请求的共享资源被占用,那么就需要一套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是用CLH队列锁实现的,即将暂时获取不到锁的线程加入到队列中。

    AQS使用一个voliate int成员变量state,来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队工作

    AQS使用CAS对该同步状态进行原子操作实现对state其值的修改。

    2. 状态变量 state

    AQS中定义了一个状态变量state,它有以下两种使用方法:

    2.1 互斥锁

    当AQS只实现为互斥锁的时候,每次只要原子更新state的值从0变为1成功了就获取了锁,可重入是通过不断把state原子更新加1实现的。

    2.2 互斥锁 + 共享锁

    当AQS需要同时实现为互斥锁+共享锁的时候,低16位存储互斥锁的状态,高16位存储共享锁的状态,主要用于实现读写锁。

    互斥锁是一种独占锁,每次只允许一个线程独占,且当一个线程独占时,其它线程将无法再获取互斥锁及共享锁,但是它自己可以获取共享锁。

    共享锁同时允许多个线程占有,只要有一个线程占有了共享锁,所有线程(包括自己)都将无法再获取互斥锁,但是可以获取共享锁。

    3. AQS队列

    AQS中维护了一个队列,获取锁失败(非tryLock())的线程都将进入这个队列中排队,等待锁释放后唤醒下一个排队的线程(互斥锁模式下)。

    4. Condition队列

    AQS中还有另一个非常重要的内部类ConditionObject,它实现了Condition接口,主要用于实现条件锁。

    当前线程调用 Condition.await()方法,将会以当前线程构造节点,并将节点从尾部加入等待队列。上述节点引用更新的过程并没有使用 CAS 保证,原因在于调用 await()方法的线程必定是 获取了锁的线程,也就是说该过程是由锁来保证线程安全的。

    当前线程调用 Condition.signal()方法,将会唤醒在等待队列中等待时间最长的节点 (首节点),在唤醒节点之前,会将节点移到同步队列中。

    5. AQS 模板方法

    模板方法同步器提供的模板方法基本上分为 3 类: 独占式获取与释放同步状态、共享式获取与释放、同步状态和查询同步队列中的等待线程情况。

    // 获取互斥锁
    public final void acquire(int arg) {
        // tryAcquire(arg)需要子类实现
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
    // 获取互斥锁可中断
    public final void acquireInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // tryAcquire(arg)需要子类实现
        if (!tryAcquire(arg))
            doAcquireInterruptibly(arg);
    }    
    // 获取共享锁
    public final void acquireShared(int arg) {
        // tryAcquireShared(arg)需要子类实现
        if (tryAcquireShared(arg) < 0)
         doAcquireShared(arg);
    }
    // 获取共享锁可中断
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // tryAcquireShared(arg)需要子类实现
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }
    // 释放互斥锁
    public final boolean release(int arg) {
        // tryRelease(arg)需要子类实现
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }
    // 释放共享锁
    public final boolean releaseShared(int arg) {
        // tryReleaseShared(arg)需要子类实现
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;

    5.1 需要子类实现的方法

    // 互斥模式下使用:尝试获取锁
    protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
    // 互斥模式下使用:尝试释放锁
    protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
    // 共享模式下使用:尝试获取锁
    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
    // 共享模式下使用:尝试释放锁
    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }
    // 如果当前线程独占着锁,返回true
    protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }
  • 相关阅读:
    reids 数据库学习
    java 8 学习二(Lambda表达式)
    java 8 学习一(概述)
    update Select 从查询的结果中更新表
    从一段文字中提取出uri信息
    Hadoop3.0磁盘均衡器
    MapReduce 程序mysql JDBC驱动类找不到原因及学习hadoop写入数据到Mysql数据库的方法
    yarn计算一个节点容量及其配置项
    YARN的capacity调度器主要配置分析
    1084 Broken Keyboard (20 分)字符串
  • 原文地址:https://www.cnblogs.com/frankcui/p/14386256.html
Copyright © 2011-2022 走看看