在最近的测试中,我遇到了一个例外,StackTrace如下:
调试定位在抛出异常源代码之前:如下:
因此,对它们的定义是,本文将结合对本问题调查过程的理解的所有方面。
希望帮助每个人更好地理解。
在引入之前,需要理解的第一个关键概念:atomicity.LET首次查看一个简单的示例:
当程序更新后,如果变量同时更新,则可能会获得比预期的其他值,例如变量,线程更新和线程B.等于2。
因为I和B线程更新变量i都是1时,这是不安全线程的更新操作。通常,我们将使用它来解决此问题,并确保多线程不会同时更新变量i。
从JDK1.5开始,Java提供了一个包裹。在此软件包中,它提供了一种原子操作类,采用简单使用,高性能和安全更新变量的方法。本文重点介绍了其中一位代表。
首先查看JDK的定义:
可能在原子上更新的对象引用。请参阅Java.util.concurrent.Atomic软件包规范,以设计原子变量的属性。
Javadoc:https://docs.oracle.com/javase/8/docs/api/java/chencurrent/Chencurrent/AtomicReference.html
角色是扮演。它提供了读写的对象参考变量。
同时,这意味着多个线程尝试更改相同的原子重新(例如比较和交换操作)不会在不一致的状态下使原子归因。
当涉及到原子包时,请提及:此处JDK为原子操作提供了许多类别。
原子类别(例如AtomicInteger)只能更新一个变量。如果要更新多个变量,则需要使用原子更新提供的参考类型提供的类来完成。
这只是对每个班级角色的简要介绍。关于软件包的重点不是本文的重点。如果您有兴趣,可以进一步阅读JDK源代码。
可以通过源代码来查看基于参考的原子操作。
一旦共享变量(类的成员变量,类的静态成员变量)会通过挥发性修改,然后具有两层语义:
从上面可以看出,包装中的类是基本使用的包装类别。换句话说,这是正确的包装。那么神圣在哪里?
让我们谈谈此类别。Java无法直接访问基础操作系统,但是可以通过本地方法访问。但是尽管如此,JVM仍然有后门。JDK中有一个不安全的类,它提供了硬件级别的原子操作。
如果对出色的开源项目进行深入分析的学生应该并不陌生。为了追求最终的性能,大多数高性能框架或项目基本上是密不可分的。不容易说使用它并不容易。您需要深入了解底层的原理,因此不建议将其直接在一般项目中使用。这也说明了为什么不属于合同,而是属于包裹。
尽管此类中的方法都是公开的,但没有办法使用它们,JDK API文档没有提供有关此类方法的任何解释。总的来说,不安全类的使用受到限制。只有信用代码才能获得此类的实例。当然,可以随意使用JDK库中的类。
不安全提供硬件级操作,例如获得内存中某个属性的位置,例如修改对象的字段值,即使它是私有的。对一般开发的需求很少。由于硬件级别的操作不安全,不安全的性能非常高,并且在追求高性能方面非常广泛使用。
CAS,即通常在设计和算法中使用的技术。该包装完全建在CAS上。没有CAS,就没有这样的包裹。在上面,您还可以看到许多比较方法。
此外,当前的处理器基本上支持CAS,但是不同制造商的实施是不同的。CAS有三个操作:
当预期值A与内部存款值V相同时,将内部存款值修改为B并返回到TRUE,否则将无需完成并返回false。
使用CAS的优点是,它不需要使用传统的锁定方法来确保线程安全性,而是取决于CAS的繁忙算法,依靠基础硬件实现来确保线程安全性。在 - 站点的开关和阻塞上,没有其他开销,它可以支持更大的并发性。(当然,CAS也有缺点,是您很忙,如果您没有获得,它将处于死周期。)
初始化过程。
以下代码是实际开发中原子重新的代码:
为了更好地显示该原理,只有核心代码由演示编写。如下所示,您可以看到原子重新的值是无效的。为什么是这样?
原因是上面的代码只是唯一的代码,但实际上,特定的参考并不是真正传递给actomicReference。因此,您看到的整数的价值仍然为无效。
我们取代了一种写作方式。创建AtomicReference时,参考对象的分配如下:代码如下:
当时,Str的引号实际上传递给了AtomicReference,因此调试仍然看到了Null。实际上,GET方法可以获取特定值。可以通过达到我们期望的调试可以看出它。如下图所示:
在上述方法中,该方法用于在课堂中定义。我相信每个人对班级并不陌生,尤其是在其中和其他方法,但是如果它不涉及原子操作或多线程开发,则可能会有不是很多方法。
这里的特殊方法将分别引入。可以通过以下源代码看到,多个重载的方法最终将调用标签方法。这种方法可能会引发中断异常。
什么时候会引发例外?
如果任何线程在当前线程之前或期间都在等待通知之前中断当前线程,则当抛出此异常时,它将删除当前线程的中断状态。
本节的重点是介绍,我们从源代码开始。
该线程被阻止异常,加上上面引入的对象的等待方法,这是一些可能引发IntertptDeexception异常方法的方法:
及其重载方法
及其重载方法
在执行上述方法后,将重新启动线程,因此可能会发生线程阻塞。因此,这些方法需要处理可能抛出的IntertptDexception异常。
thread.interrupt()
该方法将使当前线程输入阻止状态。如果另一个线程调用阻塞线,它将中断此障碍物并将其扔掉。当信号(信号)通知当前螺纹被中断。但是中断线程不等于线程生命周期的末端,只需中断当前线的阻塞状态。
通过对相关逻辑的先前理解,可以确定回到初始问题的原因必须是中断的原因。性能是由通话方法引起的。
在对日志进行了进一步的分析之后,发现了新的堆叠式Trace,如下所示:
投掷异常源代码:
调试断点屏幕截图:
上述堆栈可以在此描述中看到,表明当前使用的线程数或创建的线程数为0。我们可以大胆地猜测,由于可用线程的数量为0,因此引起了调用方法。
这里还有一个问题。我们没有在实际使用中指定它,而是默认使用的组件。那么,线程数的情况为什么存在呢?
通过分析源代码,发现Google的Goice Guice的IOC框架是由系统实施以实现DI的。注释由指定注入和指定。
所使用的绑定如下。
从上面的代码可以看出创建已完成。
由于实施了ThreadPool,因此猜测可能是由于这段时间引起的。因此,它已重新实现,但问题仍未解决。
在上述想法被阻止之后,返回到最原始的问题点,即该方法的例外和JDK注释,如下所示:
如果您不扔掉它,则可以根据方法的定义看到或停止等待。因此,决定重新分析类,放出相对完整的源代码,然后删除一些非相关代码同时。
通过重新安装类,我们发现整个代码中只有一个地方通知其他线程状态,并且代码被截获:如下:
通过注意日志的这一部分,发现了以下NPE问题。
进一步的分析是,有一个错误可以处理类中的边界值。修理后,不再发生,并且解决了问题。
在上面,我们将提及一个问题,所以什么时候会引发例外?当时的答案是:如果任何线程中断当前线程之前或期间的当前线程正在等待通知,则当前的中断状态当这种异常被抛弃时,将删除线程。这是因为正在运行的中断线程,这会导致atomicReference在调用等待方法时引发异常。
本文从一开始就分析了相关方面,并最终解决了实际工作中遇到的问题。
原始:https://juejin.cn/post/7122304178919047175
