这是针对《Java并发编程实战》(Java Concurrency in Practice)一书中的示例代码进行扩展,并且进行验证的完整代码,具体背景可看这篇文章:1-《Java并发编程实战》(Java Concurrency in Practice) 代码示例
下面的示例代码都是针对书中的(不完整的)代码扩展,不再介绍具体上下文背景(如果你也在看这本书,可以拿我这里的示例代码做个参考,并欢迎提出意见和建议)
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
/**
* @DESCRIPTION:
* @USER: shg
* @DATE: 2024/1/13 11:15
*/
public class UnsafeCachingFactorizer extends HttpServlet {
private final AtomicReference lastNumber = new AtomicReference<>();
private final AtomicReference lastFactors = new AtomicReference<>();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber.get())) {
encodeIntoResponse(resp, i, lastFactors.get());
} else {
BigInteger[] factors = factor(i);
lastNumber.set(i);
lastFactors.set(factors);
encodeIntoResponse(resp, i, lastFactors.get());
}
}
private BigInteger extractFromRequest(HttpServletRequest req) {
return new BigInteger(req.getParameter("key"));
}
private void encodeIntoResponse(HttpServletResponse resp, BigInteger i, BigInteger[] factors) throws IOException {
System.out.println(i + "--->" + Arrays.asList(factors));
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.println("");
writer.println("" + i + "因数分解结果为:" + Arrays.asList(factors));
writer.println("");
}
private BigInteger[] factor(BigInteger number) {
List factors = new ArrayList<>();
BigInteger divisor = BigInteger.valueOf(2);
while (number.compareTo(BigInteger.ONE) > 0) {
if (number.mod(divisor).equals(BigInteger.ZERO)) {
number = number.divide(divisor);
factors.add(divisor);
} else {
divisor = divisor.add(BigInteger.ONE);
}
}
return factors.toArray(new BigInteger[0]);
}
}
使用Jmeter开启两个线程组,每个线程组传递的参数不一样,但是访问同一个Servlet组件(即:访问同一个UnsafeCachingFactorizer类),然后在控制台分析输出结果,具体操作如下图:
首先要明确的是,这是一个Servlet组件(即:UnsafeCachingFactorizer类)的功能是因数分解,并且希望将最近的计算结果缓存起来,即当两个连续的请求对相同的数值进行因数分解时,可以直接使用上一次的计算结果,而无需重新计算。
并且我们可以事先知道是的129的因数分解结果应该是 3和43;24的因数分解结果应该是:2,2,2和3,现在看如下图:
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @DESCRIPTION: 解决了 UnsafeCachingFactorizer_2_5 并发访问安全性问题,但是性能降低(不能接受)
* @USER: shg
* @DATE: 2024/1/13 12:09
*/
public class SynchronizedFactorizer2_6 extends HttpServlet {
private BigInteger lastNumber;
private BigInteger[] lastFactors;
@Override
protected synchronized void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BigInteger i = extractFromRequest(req);
if (i.equals(lastNumber)) {
encodeIntoResponse(resp, i, lastFactors);
} else {
BigInteger[] factors = factor(i);
lastNumber = i;
lastFactors = factors;
encodeIntoResponse(resp, i, factors);
}
}
private BigInteger extractFromRequest(HttpServletRequest req) {
return new BigInteger(req.getParameter("key"));
}
private void encodeIntoResponse(HttpServletResponse resp, BigInteger i, BigInteger[] factors) throws IOException {
System.out.println(i + "--->" + Arrays.asList(factors));
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.println("");
writer.println("" + i + "因数分解结果为:" + Arrays.asList(factors));
writer.println("");
}
private BigInteger[] factor(BigInteger number) {
List factors = new ArrayList<>();
BigInteger divisor = BigInteger.valueOf(2);
while (number.compareTo(BigInteger.ONE) > 0) {
if (number.mod(divisor).equals(BigInteger.ZERO)) {
number = number.divide(divisor);
factors.add(divisor);
} else {
divisor = divisor.add(BigInteger.ONE);
}
}
return factors.toArray(new BigInteger[0]);
}
}
import lombok.extern.slf4j.Slf4j;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @DESCRIPTION:
* @USER: shg
* @DATE: 2024/1/13 12:44
*/
@Slf4j
public class CachedFactorizer_2_8 extends HttpServlet {
private BigInteger lastNumber;
private BigInteger[] lastFactors;
private long hits;
private long cacheHits;
public synchronized long getHits() {
return hits;
}
private synchronized long getCacheHits() {
return cacheHits;
}
public synchronized double getCacheHitRatio() {
return (double) cacheHits / (double) hits;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
BigInteger i = extractFromRequest(req);
BigInteger[] factors = null;
synchronized (this) {
++hits;
if (i.equals(lastNumber)) {
++cacheHits;
factors = lastFactors.clone();
}
}
if (factors == null) {
factors = factor(i);
synchronized (this) {
lastNumber = i;
lastFactors = factors.clone();
}
}
encodeIntoResponse(resp, i, factors);
}
private BigInteger extractFromRequest(HttpServletRequest req) {
return new BigInteger(req.getParameter("key"));
}
private BigInteger[] factor(BigInteger number) {
List factors = new ArrayList<>();
BigInteger divisor = BigInteger.valueOf(2);
while (number.compareTo(BigInteger.ONE) > 0) {
if (number.mod(divisor).equals(BigInteger.ZERO)) {
number = number.divide(divisor);
factors.add(divisor);
} else {
divisor = divisor.add(BigInteger.ONE);
}
}
return factors.toArray(new BigInteger[0]);
}
private void encodeIntoResponse(HttpServletResponse resp, BigInteger i, BigInteger[] factors) throws IOException {
System.out.println(i + "--->" + Arrays.asList(factors));
resp.setContentType("text/html;charset=utf-8");
PrintWriter writer = resp.getWriter();
writer.println("");
writer.println("" + i + "因数分解结果为:" + Arrays.asList(factors) + "\n");
writer.println(" 统计请求数量:" + getHits() + "
");
writer.println(" 缓存命中数量:" + getCacheHits() + "
");
writer.println(" 缓存命中率:" + getCacheHitRatio() + "
");
writer.println("");
}
}