Java笔记:ThreadLocal和压力测试(1)

简介: Java笔记:ThreadLocal和压力测试

数据一致性

安全感

单一数据源Single Source Of Truth

低耦合,高内聚

一致性问题:

发生在【多个主体】对【同一份数据】无法达成共识

包括:分布式一致性问题,并发问题

一致性问题解决办法(额外开销)

排队:锁、互斥锁、管程、锁障

投票:Paxos、Raft

避免:ThreadLocal

重视本质

代码是写出来是为了阅读,偶尔用于执行

ThreadLocal

定义:提供【线程局部】变量,一个线程局部变量在多个线程中,分别有独立的值(副本)

特点:简单、快速、线程安全

场景:多线程场景(资源持有、线程一致性、并发计算、线程安全)

实现:Java中用哈希表实现

应用范围:几乎所有提供多线程特征的语言

ThreadLocal基本API

构造函数  ThreadLocal<T>()
初始化    initialValue()
访问器    get/set
回收      remove

示例

构造函数

public class ThreadLocalDemo {
    public static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    public static void main(String[] args) {
        System.out.println(threadLocal.get());
        // null
        threadLocal.set(100L);
        System.out.println(threadLocal.get());
        // 100
    }
}

初始化


public static ThreadLocal<Long> threadLocal = new ThreadLocal(){
        @Override
        protected Long initialValue() {
            return 100L;
        }
    };

多线程示例

package com.demo.threadlocal;
public class ThreadLocalDemo {
    public static ThreadLocal<Long> threadLocal = new ThreadLocal() {
        @Override
        protected Long initialValue() {
            return Thread.currentThread().getId();
        }
    };
    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                System.out.println("thread: " + threadLocal.get());
                // thread: 11
            }
        }.start();
        System.out.println("main: " + threadLocal.get());
        // main: 1
        threadLocal.set(100L);
        System.out.println("main: " + threadLocal.get());
        // main: 100
        threadLocal.remove();
        System.out.println("main: " + threadLocal.get());
        // main: 1
    }
}

总结

资源持有:持有线程资源供线程的各个部分使用,全局获取,减少编程难度

线程一致性:帮助需要保持线程一致的资源(如:数据库事务),维护一致性,降低编程难度

并发计算:帮助分布式计算场景的各个线程累计局部计算结果

线程安全:帮助只考虑了单线程的程序库,无缝向多线程场景迁移


并发场景分析

例1:200QPS压测统计接口

观察:Spring框架的执行情况

目标:理解并发,竞争条件,临界区等概念

代表场景:交易

Spring代码

package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class DemoApplication {
    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }
}
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StatController {
    static Integer count = 0;
    @RequestMapping("/stat")
    public Integer stat(){
        return count;
    }
    @RequestMapping("/add")
    public Integer add(){
        count++;
        return count;
    }
}

apache2-utils压力测试工具

参考

Mac下的Web性能压力测试工具:ab(ApacheBench)

Mac下自带apache

查看版本号
$apachectl -v
$ ab -V
使用方式
$ ab -n 请求数 -c 并发数  URL
eg:
$ ab -n 10000 -c 1 localhost:8080/add
$ curl localhost:8080/stat
10000
$ ab -n 10000 -c 10 localhost:8080/add
$ curl localhost:8080/stat
9250

分析:


理想情况:

a=0

A:read(a) -> A:write(a+1)  a=1

B:read(a) -> B:write(a+1)  a=2


并发情况

a=0

A:read(a) -> B:read(a) -> A:write(a+1) -> B:write(a+1) a=1


并发:多个程序同时执行

竞争条件:多个进程(线程)同时访问同一个内存资源,最终的执行结果依赖于多个进程执行时的精准时序

临界区:访问共享内存的程序片段

1、让add方法增加延迟

package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StatController {
    static Integer count = 0;
    @RequestMapping("/stat")
    public Integer stat(){
        return count;
    }
    @RequestMapping("/add")
    public Integer add() throws InterruptedException {
        Thread.sleep(100L);
        count++;
        return count;
    }
}
$ ab -n 10000 -c 100 localhost:8080/add
$ curl localhost:8080/stat
9097

2、加锁测试

package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StatController {
    static Integer count = 0;
    @RequestMapping("/stat")
    public Integer stat(){
        return count;
    }
    @RequestMapping("/add")
    public Integer add() throws InterruptedException {
        // Thread.sleep(100L);
        // count++;
        __add();
        return count;
    }
    synchronized void __add() throws InterruptedException {
        Thread.sleep(100L);
        count++;
    }
}

如果10000个请求会很慢,所以减少请求次数测试

$ ab -n 100 -c 10 localhost:8080/add
$ curl localhost:8080/stat
100

3、使用ThreadLocal

package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class StatController {
    static ThreadLocal<Integer> count = new ThreadLocal(){
        @Override
        protected Object initialValue() {
            return 0;
        }
    };
    @RequestMapping("/stat")
    public Integer stat(){
        return count.get();
    }
    @RequestMapping("/add")
    public Integer add() throws InterruptedException {
        // Thread.sleep(100L);
        // count++;
        __add();
        return count.get();
    }
    void __add() throws InterruptedException {
        Thread.sleep(100L);
        count.set(count.get()+1);
    }
}
ab -n 10000 -c 100 localhost:8080/add
$ curl localhost:8080/stat
100
$ curl localhost:8080/stat
99
$ curl localhost:8080/stat
100
$ curl localhost:8080/stat
99
$ curl localhost:8080/stat
99

总结

  • 基于线程池模型synchronize(排队操作很危险)
  • 使用ThreadLocal收集数据很快速且安全(如何收集数据)

相关文章
|
4月前
|
算法 IDE Java
Java 项目实战之实际代码实现与测试调试全过程详解
本文详细讲解了Java项目的实战开发流程,涵盖项目创建、代码实现(如计算器与汉诺塔问题)、单元测试(使用JUnit)及调试技巧(如断点调试与异常排查),帮助开发者掌握从编码到测试调试的完整技能,提升Java开发实战能力。
444 0
|
5月前
|
安全 Java 测试技术
Java 项目实战中现代技术栈下代码实现与测试调试的完整流程
本文介绍基于Java 17和Spring技术栈的现代化项目开发实践。项目采用Gradle构建工具,实现模块化DDD分层架构,结合Spring WebFlux开发响应式API,并应用Record、Sealed Class等新特性。测试策略涵盖JUnit单元测试和Testcontainers集成测试,通过JFR和OpenTelemetry实现性能监控。部署阶段采用Docker容器化和Kubernetes编排,同时展示异步处理和反应式编程的性能优化。整套方案体现了现代Java开发的最佳实践,包括代码实现、测试调试
209 0
|
5月前
|
人工智能 Java 测试技术
Java or Python?测试开发工程师如何选择合适的编程语言?
测试工程师如何选择编程语言?Java 还是 Python?多位资深专家分享建议:Python 入门简单、开发效率高,适合新手及自动化测试;Java 生态成熟,适合大型项目和平台开发。建议结合公司技术栈、个人基础及发展方向选择。长远来看,两者兼通更佳,同时关注 Go 等新兴语言。快速学习与实践才是关键。
|
7月前
|
存储 安全 Java
深入探究Java中ThreadLocal的工作原理和用途
总结起来,ThreadLocal是Java多线程编程中一个非常有用的工具,通过为每个线程分配独立的变量副本,实现线程隔离,避免资
165 9
|
8月前
|
存储 Java
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
前言 下面,跟上主播的节奏,马上开始ThreadLocal源码的阅读( ̄▽ ̄)" 内部结构 如下图所示,我们可以知道,每个线程,都有自己的threadLocals字段,指向ThreadLocalMap
525 81
【源码】【Java并发】【ThreadLocal】适合中学者体质的ThreadLocal源码阅读
|
8月前
|
存储 缓存 安全
【Java并发】【ThreadLocal】适合初学体质的ThreadLocal
ThreadLocal 是 Java 中用于实现线程本地存储(Thread-Local Storage)的核心类,它允许每个线程拥有自己独立的变量副本,从而在多线程环境中实现线程隔离,避免共享变量带来的线程安全问题。
217 9
【Java并发】【ThreadLocal】适合初学体质的ThreadLocal
|
9月前
|
缓存 监控 负载均衡
如何提升 API 性能:来自 Java 和测试开发者的优化建议
本文探讨了如何优化API响应时间,提升用户体验。通过缓存(如Redis/Memcached)、减少数据负载(REST过滤字段或GraphQL精确请求)、负载均衡(Nginx/AWS等工具)、数据压缩(Gzip/Brotli)、限流节流、监控性能(Apipost/New Relic等工具)、升级基础设施、减少第三方依赖、优化数据库查询及采用异步处理等方式,可显著提高API速度。快速响应的API不仅让用户满意,还能增强应用整体性能。
|
9月前
|
存储 设计模式 Java
重学Java基础篇—ThreadLocal深度解析与最佳实践
ThreadLocal 是一种实现线程隔离的机制,为每个线程创建独立变量副本,适用于数据库连接管理、用户会话信息存储等场景。
294 5
|
10月前
|
存储 数据可视化 测试技术
一个测试工程师的实战笔记:我是如何在Postman和Apipost之间做出选择的?
优秀的API测试工具应该具备: 分层设计:既有可视化操作,也开放代码层深度定制 场景感知:自动识别加密需求推荐处理方案 协议包容:不强迫开发者为了不同协议切换工具 数据主权:允许自主选择数据存储位置
270 7
|
11月前
|
存储 Java 开发者
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。
本文详细介绍了 Java 中 `toString()` 方法的重写技巧及其重要
578 10
【潜意识Java】深入详细理解分析Java中的toString()方法重写完整笔记总结,超级详细。

热门文章

最新文章