深入了解JAVA中锁的重要性

并发编程时首先考虑的是线程安全问题,线程安全,指的是多线程访问下,某个函数被多个线程调用多次,都能够正确处理各个线程的局部变量,并且计算结果正确。

解决线程安全问题,一般有三种方式:

    使用 ThreadLocal 避免线程共享变量

    使用 synchronized 和 lock 进行同步控制

    使用原子类声明变量

锁用来做同步控制,可以控制多个线程访问共享资源的顺序,Java 中内置的锁 synchronized 一直饱受争议,虽然在 JDK1.6 之后有所优化,但是基于隐式的获取锁和释放锁操作,一定程度上减少了获取锁和释放锁的可操作性和灵活性。

Java 中的 Lock 锁是基于队列同步器 AQS (AbstractQueuedSynchronized) 实现的,AQS 是构建 ReentrantLock、Semaphore、ReentrantReadWriteLock、CountDownLatch 和 FutureTask 的基础框架。

队列同步器是用来做同步控制的,需要最基本的需要两点功能:

    同步状态的获取

    同步状态的释放

AQS 提供了那些方法对同步状态进行管理呢?

    getState():返回同步状态的当前值;

    setState(int newState):设置当前同步状态;

    compareAndSetState(int expect, int update):使用 CAS 设置当前状态,该方法能够保证状态设置的原子性;

    tryAcquire(int arg):独占式获取同步状态,获取同步状态成功后,其他线程需要等待该线程释放同步状态才能获取同步状态;

    tryRelease(int arg):独占式释放同步状态;

    tryAcquireShared(int arg):共享式获取同步状态,返回值大于等于 0 则表示获取成功,否则获取失败;

    tryReleaseShared(int arg):共享式释放同步状态;

    isHeldExclusively():当前同步器是否在独占式模式下被线程占用,一般该方法表示是否被当前线程所独占;

    acquire(int arg):独占式获取同步状态,如果当前线程获取同步状态成功,则由该方法返回,否则,将会进入同步队列等待,该方法将会调用可重写的 tryAcquire(int arg) 方法;

    acquireInterruptibly(int arg):与 acquire(int arg) 相同,但是该方法响应中断,当前线程为获取到同步状态而进入到同步队列中,如果当前线程被中断,则该方***抛出 InterruptedException 异常并返回;

    tryAcquireNanos(int arg,long nanos):超时获取同步状态,如果当前线程在 nanos 时间内没有获取到同步状态,那么将会返回 false,已经获取则返回 true;

    acquireShared(int arg):共享式获取同步状态,如果当前线程未获取到同步状态,将会进入同步队列等待,与独占式的主要区别是在同一时刻可以有多个线程获取到同步状态;

    acquireSharedInterruptibly(int arg):共享式获取同步状态,响应中断;

    tryAcquireSharedNanos(int arg, long nanosTimeout):共享式获取同步状态,增加超时限制;

    release(int arg):独占式释放同步状态,该方***在释放同步状态之后,将同步队列中第一个节点包含的线程唤醒;

    releaseShared(int arg):共享式释放同步状态。

AQS 是怎么实现的呢?

队列同步器依赖一个 FIFO 双向队列来完成同步状态的管理。

CLH 队列同步器结构如下:



当前线程获取同步状态失败时,将当前线程和等待状态信息构成一个节点 (Node)并将其加入同步队列,同时阻塞当前线程。

当同步状态释放时,会唤醒下一个节点,并设置成首节点,使其再去获取同步状态。
Node 节点有哪些属性?

    static final class Node {
     
    /** Marker to indicate a node is waiting in shared mode */
     
    static final Node SHARED = new Node();
     
    /** Marker to indicate a node is waiting in exclusive mode */
     
    static final Node EXCLUSIVE = null;
     
    /** 值为1,由于同步队列中等待的线程超时或者被中断,需要到同步队列中取消等待,节点进入该状态将不会变*/
     
    static final int CANCELLED = 1;
     
    /**后继节点的线程处于阻塞状态,而如果当前节点的线程如果释放同步状态或者被取消,通知后继节点,使得后继节点可以运行*/
     
    static final int SIGNAL = -1;
     
    /** 值为-2 节点在等待队列中,节点线程等待在Condition上,当其他线程对 Condition 调用了 signal() 方法后,该节点会从等待队列转移到同步队列中,进行同步状态的获取 */
     
    static final int CONDITION = -2;
     
    /**
    表示下次共享状态的获取将会无跳转的传播下去
    */
     
    static final int PROPAGATE = -3;
     
    volatile int waitStatus;
     
    volatile Node prev;
     
    volatile Node next;
     
    volatile Thread thread;
     
    /** 等待队列中的后继节点*/
     
    Node nextWaiter;
     
    final boolean isShared() {
     
    return nextWaiter == SHARED;
     
    }
     
    final Node predecessor() throws NullPointerException {
     
    Node p = prev;
     
    if (p == null)
     
    throw new NullPointerException();
     
    else
     
    return p;
     
    }
     
    Node(Thread thread, Node mode) { // Used by addWaiter
     
    this.nextWaiter = mode;
     
    this.thread = thread;
     
    }
     
    Node(Thread thread, int waitStatus) { // Used by Condition
     
    this.waitStatus = waitStatus;
     
    this.thread = thread;
     
    }
     
    }


#java#
全部评论

相关推荐

09-13 17:25
亲切的00后在笔试:我也遇到了,所以我早他一步查看图片
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务