2-《Java并发编程实战》(Java Concurrency in Practice) 代码示例

文章详细分析了《Java并发编程实战》中的代码片段,探讨了如何扩展和优化Servlet中的并发处理,通过UnsafeCachingFactorizer类实现因数分解功能,并对比了不同版本的线程安全性和缓存策略对性能的影响。
摘要由CSDN通过智能技术生成

说明

        这是针对《Java并发编程实战》(Java Concurrency in Practice)一书中的示例代码进行扩展,并且进行验证的完整代码,具体背景可看这篇文章:1-《Java并发编程实战》(Java Concurrency in Practice) 代码示例

        下面的示例代码都是针对书中的(不完整的)代码扩展,不再介绍具体上下文背景(如果你也在看这本书,可以拿我这里的示例代码做个参考,并欢迎提出意见和建议)

程序清单2-5

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<BigInteger> lastNumber = new AtomicReference<>();
    private final AtomicReference<BigInteger[]> 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("<html><body>");
        writer.println("<h1>" + i + "因数分解结果为:" + Arrays.asList(factors));
        writer.println("</body></html>");
    }

    private BigInteger[] factor(BigInteger number) {
        List<BigInteger> 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,现在看如下图:

程序清单2-6

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("<html><body>");
        writer.println("<h1>" + i + "因数分解结果为:" + Arrays.asList(factors));
        writer.println("</body></html>");
    }

    private BigInteger[] factor(BigInteger number) {
        List<BigInteger> 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]);
    }
}

程序清单2-8

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<BigInteger> 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("<html><body>");
        writer.println("<h1>" + i + "因数分解结果为:" + Arrays.asList(factors) + "\n");
        writer.println("<h1> 统计请求数量:" + getHits() + "</h1>");
        writer.println("<h1> 缓存命中数量:" + getCacheHits() + "</h1>");
        writer.println("<h1> 缓存命中率:" + getCacheHitRatio() + "</h1>");
        writer.println("</body></html>");
    }
}

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值