回眸重探锁机制

  • void acquireInterruptibly(int arg):获取锁,抛出打断异常

public final void acquireInterruptibly(int arg)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (!tryAcquire(arg))
doAcquireInterruptibly(arg);
}

  • boolean tryAcquireNanos(int arg, long nanosTimeout):获取锁存在最长时间,抛出打断异常

public final boolean tryAcquireNanos(int arg, long nanosTimeout)
throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquire(arg) ||
doAcquireNanos(arg, nanosTimeout);
}

可见,如果tryAcquire返回true时,相应方法也就结束了,线程后续方法即可继续执行,也就是获取锁了;而如果返回false时,后面方法则很大可能会触发线程暂停

protected boolean tryAcquire(int arg) {
throw new UnsupportedOperationException();
}

tryAcquire方法需要用户自实现;现有锁都是通过AQS中state整数变量来实现的

6.2.2 释放资源

public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}

这里tryRelease返回true时,才有可能通知其它线程去竞争资源;

protected boolean tryRelease(int arg) {
throw new UnsupportedOperationException();
}

需要用户自实现;现有锁都是通过AQS中state整数变量来实现的

6.3 独占锁

6.3.1 获取资源

同样存在不同的入口

  • void acquireShared(int arg):获取共享锁

public final void acquireShared(int arg) {
if (tryAcquireShared(arg) < 0)
doAcquireShared(arg);
}

  • void acquireSharedInterruptibly(int arg):获取共享锁,会抛出打断异常

public final void acquireSharedInterruptibly(int arg) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
if (tryAcquireShared(arg) < 0)
doAcquireSharedInterruptibly(arg);
}

  • boolean tryAcquireSharedNanos(int arg, long nanosTimeout):获取锁有时间限制,超过则结束返回是否获取成功;会抛出打断异常

public final boolean tryAcquireSharedNanos(int arg, long nanosTimeout) throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
return tryAcquireShared(arg) >= 0 || doAcquireSharedNanos(arg, nanosTimeout);
}

这里获取资源的条件是受 tryAcquireShared的返回值来控制的,>=0时,线程继续执行,否则,很可能会线程暂停; tryAcquireShared方法需要用户实现,也会通过AQS中state变量来处理

protected int tryAcquireShared(int arg) {
throw new UnsupportedOperationException();
}

6.3.2 释放资源

释放时也只有一个入口

public final boolean releaseShared(int arg) {
if (tryReleaseShared(arg)) {
doReleaseShared();
return true;
}
return false;
}

tryReleaseShared返回true时,才有可能去唤醒其它线程去竞争资源;也需要用户实现,同样,也是基于AQS中state变量来控制

protected boolean tryReleaseShared(int arg) {
throw new UnsupportedOperationException();
}

6.4 可重入

可重入性,是持有资源者,再次自动持有资源的行为;这些在独占锁锁中,只需要线程判断即可,而共享锁,则是不仅仅需要线程判断,还需要一些列共享持有者单独比对,无疑当前线程比对可以优化这个过程;线程的存储AQS已经提供方法,这些方法来源于其继承的抽象类AbstractOwnableSynchronizer,而是否持有,则由方法isHeldExclusively提供,而这个方法需要用户实现

protected boolean isHeldExclusively() {
throw new UnsupportedOperationException();
}

6.5 数据变化

数据变化,涉及到几个重要方法:

  • addWaiter:从双端队列,队尾加入节点;线程获取资源失败时,会生成节点并加入队列;而条件锁在等待唤醒时被唤醒后,通过enq方法加入队列
  • shouldParkAfterFailedAcquire:决定是否暂停当前线程,在循环中处理
  • parkAndCheckInterrupt:暂停线程,并返回打断状态且重置打断状态
  • unparkSuccessor:唤醒等待资源的线程,进行资源竞争获取
6.5.1 添加节点

独占资源

image.png

共享资源

image.png

条件资源

image.png

6.5.2 线程执行异常或者被打断

独占资源

image.png

共享资源

image.png

6.5.3 等待状态

相对于5.5.2,其中变化的仅仅waitStatus = -1

6.5.4 条件唤醒状态

这是条件锁特有状态;实现原理是:首先节点在条件单列表条件链表中等待;两个等待循环条件为是否在资源竞争的双向列表中(就是独占/共享的队列),如果被唤醒,其就会在此队列了,然后调用独占锁的逻辑获取锁 其关键方法:

  • isOnSyncQueue:是否存在AQS中的双向队列中,被唤醒后存在
  • doSignal:移除条件单向列表列中头节点,并改变状态waitStatus=0,加入AQS双向列表中去
6.6 条件锁使用条件

final int fullyRelease(Node node) {
try {
int savedState = getState();
if (release(savedState))
return savedState;
throw new IllegalMonitorStateException();
} catch (Throwable t) {
node.waitStatus = Node.CANCELLED;
throw t;
}
}

这个方法是在获取资源时均会调用的方法;会首先释放state个资源使用权,然后通过加入队列后通过相应方法获取state个使用权;释放不了state个使用权会直接报异常;因此,条件锁使用时,必须在当前已经获取资源使用权的情况使用下,且仅仅只有其自己获取资源使用权;

6.7 小结

其在性能上有许多要学习的地方:比如在获取资源执行权时,并没有立刻去暂停线程;在状态变化过程中,也没有仅仅考虑当前状态,也进行有可能的唤醒线程竞争、去除无效资源等;整体采用自旋+CAS机制处理;

7 android中的锁

7.1 synchronized关键字

上面介绍锁概念时大致介绍了这个关键字,其通过编译时在代码块加入同步指令处理的;是以特殊的一个对象作为锁的标志:

  • 修饰实例方法,作用于当前实例加锁,进入同步代码前要获得当前实例的锁
  • 修饰静态方法,作用于当前类对象加锁,进入同步代码前要获得当前类对象的锁
  • 修饰代码块,指定加锁对象,对给定对象加锁,进入同步代码库前要获得给定对象的锁。

通过锁对象来实现条件策略;这写方法来源于Object的wait/notify/notifyAll方法

7.2 ReentrantLock锁

可重入锁、独占锁;有两种模式:公平、非公平,可通过构造器进行设定;公平不公平,就是在获取资源执行权时,是否按照排队顺序优先获取来定的

公平锁

final void lock() {
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (!hasQueuedPredecessors() &&
compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0)
throw new Error(“Maximum lock count exceeded”);
setState(nextc);
return true;
}
return false;
}

这里有两种获取锁的方式:

  1. 当前state=0,AQS队列中无排队线程,且CAS操作state成功,则把记录当前线程
  2. 当前线程为上次纪律线程,则把state再次增加

为什么公平,就是因为,可获取资源时,先检查当前排队队列是否为空,为空才会获取

非公平锁

final void lock() {
if (compareAndSetState(0, 1))
setExclusiveOwnerThread(Thread.currentThread());
else
acquire(1);
}

protected final boolean tryAcquire(int acquires) {
return nonfairTryAcquire(acquires);
}

final boolean nonfairTryAcquire(int acquires) {
final Thread current = Thread.currentThread();
int c = getState();
if (c == 0) {
if (compareAndSetState(0, acquires)) {
setExclusiveOwnerThread(current);
return true;
}
}
else if (current == getExclusiveOwnerThread()) {
int nextc = c + acquires;
if (nextc < 0) // overflow
throw new Error(“Maximum lock count exceeded”);
setState(nextc);
return true;
}
return false;
}

获取锁资源成功条件

  1. 对state进行CAS操作,成功;记录线程
  2. 如果当前资源可被获取也即state = 0时,对state进行CAS操作成功;记录线程
  3. 当前线程为记录线程

唤醒竞争

protected final boolean tryRelease(int releases) {
int c = getState() - releases;
if (Thread.currentThread() != getExclusiveOwnerThread())
throw new IllegalMonitorStateException();
boolean free = false;
if (c == 0) {
free = true;
setExclusiveOwnerThread(null);
}
setState©;
return free;
}

state为0时,才会唤醒其它线程进行竞争;而不为0时,只是进行减少,这于可重入处理时相加相对应;所以加锁和解锁要成对出现

7.3 ReentrantReadWriteLock锁

可重入锁;独占锁和共享锁共存; 读写锁;其读锁调用AQS的共享资源方法,写锁调用AQS独占资源方法;其也存在两种模式:公平、非公平,可通过构造器进行设定;

公平/非公平模式

  1. 对于写锁,公平时查看当前是否排队的,排队优先,非公平时,尝试获取资源的线程优先
  2. 对于读锁,公平模式时同样查看当前是否有排队的,排队优先,非公平时,根据排队等待的对头是否为独占节点

也即是依靠如下方法:返回true表示公平模式

abstract boolean readerShouldBlock();
abstract boolean writerShouldBlock()

条件锁

  1. 读锁:直接抛出UnsupportedOperationException异常
  2. 写锁:使用AQS实现类调用newCondition生成

state状态

读锁,通过state的低16位来确定个数;写锁通过state的高16位来确定

独占资源处理

protected final boolean tryRelease(int releases) {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
int nextc = getState() - releases;
boolean free = exclusiveCount(nextc) == 0;
if (free)
setExclusiveOwnerThread(null);
setState(nextc);
return free;
}

唤醒处理和ReentrantLock没有区别

protected final boolean tryAcquire(int acquires) {
Thread current = Thread.currentThread();
int c = getState();
int w = exclusiveCount©;
if (c != 0) {
if (w == 0 || current != getExclusiveOwnerThread())
return false;
if (w + exclusiveCount(acquires) > MAX_COUNT)
throw new Error(“Maximum lock count exceeded”);
setState(c + acquires);
return true;
}
if (writerShouldBlock() ||
!compareAndSetState(c, c + acquires))
return false;
setExclusiveOwnerThread(current);
return true;
}

获取资源时,逻辑如下:

  1. state!=0,而排队的独占节点为0,或者不为记录线程;则获取失败,这时优先读锁
  2. state!=0,而排队中存在独占节点且为当前线程已经获得执行权,则获取成功
  3. 写公平模式或者CAS操作state失败,获取失败,否则成功

共享资源处理

这个相对于独占就复杂多了

protected final boolean tryReleaseShared(int unused) {
Thread current = Thread.currentThread();
if (firstReader == current) {
if (firstReaderHoldCount == 1)
firstReader = null;
else
firstReaderHoldCount–;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
int count = rh.count;
if (count <= 1) {
readHolds.remove();
if (count <= 0)
throw unmatchedUnlockException();
}
–rh.count;
}
for (;😉 {
int c = getState();
int nextc = c - SHARED_UNIT;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}

对是否需要唤醒时,首先需要处理共享的数据,然后才是state的CAS操作且是循环操作

  • 共享数据: firstReader共享节点的第一个线程对象,firstReader线程对象对应的重入数目firstReaderHoldCount;cachedHoldCounter:最后一个共享节点对象线程tid和持有重入数目;readHolds为HoldCounte线程存储
  • CAS循环操作的原因:因为读操作可能同时存在多个,保证state的安全

protected final int tryAcquireShared(int unused) {
Thread current = Thread.currentThread();
int c = getState();
if (exclusiveCount© != 0 &&
getExclusiveOwnerThread() != current)
return -1;
int r = sharedCount©;
if (!readerShouldBlock() &&
r < MAX_COUNT &&
compareAndSetState(c, c + SHARED_UNIT)) {
if (r == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
HoldCounter rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
cachedHoldCounter = rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
}
return 1;
}
return fullTryAcquireShared(current);
}

final int fullTryAcquireShared(Thread current) {
HoldCounter rh = null;
for (;😉 {
int c = getState();
if (exclusiveCount© != 0) {
if (getExclusiveOwnerThread() != current)
return -1;
} else if (readerShouldBlock()) {
if (firstReader == current) {
} else {
if (rh == null) {
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current)) {
rh = readHolds.get();
if (rh.count == 0)
readHolds.remove();
}
}
if (rh.count == 0)
return -1;
}
}
if (sharedCount© == MAX_COUNT)
throw new Error(“Maximum lock count exceeded”);
if (compareAndSetState(c, c + SHARED_UNIT)) {
if (sharedCount© == 0) {
firstReader = current;
firstReaderHoldCount = 1;
} else if (firstReader == current) {
firstReaderHoldCount++;
} else {
if (rh == null)
rh = cachedHoldCounter;
if (rh == null || rh.tid != getThreadId(current))
rh = readHolds.get();
else if (rh.count == 0)
readHolds.set(rh);
rh.count++;
cachedHoldCounter = rh; // cache for release
}
return 1;
}
}
}

共享资源获取,逻辑如下:

  1. 队列中存在独占节点,此时不是记录线程,则获取失败
  2. 非公平模式且CAS操作state成功,则获取成功
  3. 循环进行处理以下流程
  4. 队列中存在独占节点,此时不是记录线程,则获取失败
  5. 不存在独占节点且公平模式且不是第一个共享节点,持有锁数目为0时,获取失败
  6. CAS操作成功,则获取成功
    在获取成功后,均会记录数据:
  7. 无共享节点,记录第一个线程对象,记录线程持有资源数目为1
    自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。

img

img

img

img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)

总结

其实要轻松掌握很简单,要点就两个:

  1. 找到一套好的视频资料,紧跟大牛梳理好的知识框架进行学习。
  2. 多练。 (视频优势是互动感强,容易集中注意力)

你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。

[外链图片转存中…(img-Nkh53MCo-1713535401619)]

《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要在Unity中使用脚本修改相机的Render Shadows属性,可以使用以下代码: ```csharp using UnityEngine; public class ModifyCameraShadows : MonoBehaviour { public Camera cameraToModify; void Start() { if (cameraToModify == null) { cameraToModify = Camera.main; } cameraToModify.renderingPath = RenderingPath.Forward; // 设置渲染路径为Forward if (cameraToModify != null) { cameraToModify.renderingPath = RenderingPath.Forward; // 设置渲染路径为Forward cameraToModify.useOcclusionCulling = true; // 开启遮挡剔除 cameraToModify.allowHDR = true; // 允许HDR cameraToModify.allowMSAA = true; // 允许多重采样 cameraToModify.allowDynamicResolution = true; // 允许动态分辨率 cameraToModify.depthTextureMode = DepthTextureMode.Depth; // 开启深度纹理模式 cameraToModify.clearFlags = CameraClearFlags.SolidColor; // 设置背景色 cameraToModify.backgroundColor = Color.black; // 设置背景颜色为黑色 cameraToModify.farClipPlane = 1000.0f; // 设置远裁剪面 cameraToModify.nearClipPlane = 0.3f; // 设置近裁剪面 cameraToModify.usePhysicalProperties = true; // 开启物理属性 cameraToModify.useJitteredProjectionMatrixForTransparentRendering = true; // 开启透明渲染 cameraToModify.allowHDR = true; // 允许HDR cameraToModify.allowMSAA = true; // 允许多重采样 cameraToModify.allowDynamicResolution = true; // 允许动态分辨率 cameraToModify.renderShadows = true; // 开启阴影渲染 cameraToModify.shadowDistance = 100.0f; // 设置阴影距离 cameraToModify.shadowCullingMask = LayerMask.GetMask("Default"); // 设置渲染阴影的层级 cameraToModify.useJitteredProjectionMatrixForTransparentRendering = true; // 开启透明渲染 } } } ``` 在这个脚本中,我们使用`cameraToModify`变量来引用我们想要修改的相机。如果没有指定`cameraToModify`,脚本将会修改`Camera.main`。 我们可以设置相机的各种属性,包括渲染路径、遮挡剔除、多重采样、动态分辨率、深度纹理模式、背景色、远裁剪面、近裁剪面、物理属性等等。最后,我们可以使用`cameraToModify.renderShadows = true`开启阴影渲染,并使用`cameraToModify.shadowCullingMask`来设置渲染阴影的层级。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值