深入了解JAVA中锁的重要性

举报
陈橘又青 发表于 2022/06/30 19:54:50 2022/06/30
【摘要】 并发编程时首先考虑的是线程安全问题,线程安全,指的是多线程访问下,某个函数被多个线程调用多次,都能够正确处理各个线程的局部变量,并且计算结果正确。解决线程安全问题,一般有三种方式:使用 ThreadLocal 避免线程共享变量使用 synchronized 和 lock 进行同步控制使用原子类声明变量锁用来做同步控制,可以控制多个线程访问共享资源的顺序,Java 中内置的锁 synchron...

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

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

使用 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;

}

}




【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。