1.线程安全性
一.原子性
无状态对象一定是线程安全的,假设我们希望增加一个“命中计数器”来统计所处理的请求数量,一种直观的方法是增加一个long类型的域。
@NotThreadSafe public class UnsafeCountingFactorizer extends GenericServlet implements Servlet { private long count = 0; public long getCount() { return count; } public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = factor(i); ++count; encodeIntoResponse(resp, factors); } void encodeIntoResponse(ServletResponse res, BigInteger[] factors) { } BigInteger extractFromRequest(ServletRequest req) { return new BigInteger("7"); } BigInteger[] factor(BigInteger i) { // Doesn't really factor return new BigInteger[] { i }; } }
UnsafeCountingFactorizer 并非线程安全的,因为递增操作++count并非原子的。它包含三个独立的操作:读取count的值,将值加1,然后将计算结果写入count。
1.竞态条件
UnsafeCountingFactorizer 中存在多个竞态条件。当某个计算的正确性取决于多个线程的交替执行时序时,那么就会发生竞态条件。最常见的竞态条件类型就是“先检查后执行”操作。
2.复合操作
@ThreadSafe public class CountingFactorizer extends GenericServlet implements Servlet { private final AtomicLong count = new AtomicLong(0); public long getCount() { return count.get(); } public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); BigInteger[] factors = factor(i); count.incrementAndGet(); encodeIntoResponse(resp, factors); } void encodeIntoResponse(ServletResponse res, BigInteger[] factors) {} BigInteger extractFromRequest(ServletRequest req) {return null; } BigInteger[] factor(BigInteger i) { return null; } }
通过用AtoimcLong来代替long类型的计数器,能够确保所有对计数器状态的访问操作都是原子的。
二.加锁机制
假设我们希望提升Servlet的性能:将最近的计算结果缓存起来,当两个连续的请求对相同的数值进行因数分解时,可以直接使用上一次的计算结果,而无需重新计算。
@NotThreadSafe public class UnsafeCachingFactorizer extends GenericServlet implements Servlet { private final AtomicReference<BigInteger> lastNumber = new AtomicReference<BigInteger>(); private final AtomicReference<BigInteger[]> lastFactors = new AtomicReference<BigInteger[]>(); public void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); if (i.equals(lastNumber.get())) encodeIntoResponse(resp, lastFactors.get()); else { BigInteger[] factors = factor(i); lastNumber.set(i); lastFactors.set(factors); encodeIntoResponse(resp, factors); } } void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) { } BigInteger extractFromRequest(ServletRequest req) { return new BigInteger("7"); } BigInteger[] factor(BigInteger i) { // Doesn't really factor return new BigInteger[]{i}; } }
这种方法并不正确。尽管这些原子引用本身都是线程安全的,但在UnsafeCachingFactorizer 中存在着竞态条件,这可能产生错误的结果。尽管对set方法每次调用都是原子的,但仍然无法同时更新lastNumber和lastFactores。在线程A获取这两个值的过程中,线程B可能修改了它们。
1.内置锁
Java提供了一种内置的锁机制来支持原子性:同步代码块。
@ThreadSafe public class SynchronizedFactorizer extends GenericServlet implements Servlet { @GuardedBy("this") private BigInteger lastNumber; @GuardedBy("this") private BigInteger[] lastFactors; public synchronized void service(ServletRequest req, ServletResponse resp) { BigInteger i = extractFromRequest(req); if (i.equals(lastNumber)) encodeIntoResponse(resp, lastFactors); else { BigInteger[] factors = factor(i); lastNumber = i; lastFactors = factors; encodeIntoResponse(resp, factors); } } void encodeIntoResponse(ServletResponse resp, BigInteger[] factors) { } BigInteger extractFromRequest(ServletRequest req) { return new BigInteger("7"); } BigInteger[] factor(BigInteger i) { // Doesn't really factor return new BigInteger[] { i }; } }
2.重入
三.用锁来保护状态
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
2020-01-05 Collections 工具类和 Arrays 工具类常见方法
2020-01-05 Java基础知识