深入了解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;
}
}
- 点赞
- 收藏
- 关注作者
评论(0)