java如何理解锁_深入理解 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 队列同步器结构如下:

5681f47f063cb71ed37b6f2768e88d89.png

当前线程获取同步状态失败时,将当前线程和等待状态信息构成一个节点 (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;

}

}

CLH 队列的入队出队是怎么实现的?

28e8a417f94c99f97926601b42d9252c.png

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值