Java笔记ThreadLocal和压力测试

网友投稿 532 2022-05-30

数据一致性

安全感

单一数据源Single Source Of Truth

低耦合,高内聚

一致性问题:

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

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

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

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

投票:Paxos、Raft

避免:ThreadLocal

重视本质

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

ThreadLocal

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

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

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

实现:Java中用哈希表实现

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

ThreadLocal基本API

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

1

2

3

4

示例

构造函数

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

1

2

3

4

5

6

7

8

9

10

11

12

13

初始化

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

1

2

3

4

5

6

多线程示例

package com.demo.threadlocal; public class ThreadLocalDemo { public static ThreadLocal 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

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

总结

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

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

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

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

并发场景分析

例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); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

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; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

分析:

理想情况: 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

2

3

4

5

6

7

8

9

并发:多个程序同时执行

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

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

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; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

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

1

2

3

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++; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

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

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

1

2

3

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 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); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

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

1

2

3

4

5

6

7

8

9

10

11

12

13

总结

基于线程池模型synchronize(排队操作很危险)

使用ThreadLocal收集数据很快速且安全(如何收集数据)

ThreadLocal同步

package com.example.demo; // 自定义一个引用类型 public class Value { private T value; public void set(T _value) { value = _value; } public T get() { return value; } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

改造后

package com.example.demo; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashSet; @RestController public class StatController { static HashSet> set = new HashSet<>(); static ThreadLocal> count = new ThreadLocal(){ @Override protected Value initialValue() { Value value = new Value<>(); value.set(0); addSet(value); return value; } }; synchronized static void addSet(Value value){ // 临界区操作 set.add(value); } void __add() throws InterruptedException { Thread.sleep(100L); Value value = count.get(); value.set(value.get() + 1); } @RequestMapping("/stat") public Integer stat(){ return set.stream().map(x->x.get()).reduce((a, b) -> a+b).get(); } @RequestMapping("/add") public Integer add() throws InterruptedException { __add(); return count.get().get(); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

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

1

2

3

总结

完全避免同步(困难)

缩小同步范围(简单)+ ThreadLocal解决问题

源码分析

Quartz: SimpleSemaphore

MyBatis: SqlSessionManager

Spring

本地事务

A Atomic 原子性 操作不可分割

C Consistency 一致性 任何时刻数据都能保持一致

I Isolation 隔离性 多事务并发执行的时序不影响结果

Java笔记:ThreadLocal和压力测试

D Durability 持久性 对数据接收的存储是永久的

自定义实现ThreadLocal

package com.demo.threadlocal; import java.util.HashMap; import java.util.concurrent.atomic.AtomicInteger; /** * 自定义实现ThreadLocal * * @param */ public class MyThreadLocal { // 自增接口保证唯一性 static AtomicInteger atomic = new AtomicInteger(); // 高德纳 hash值 Integer threadLocalHash = atomic.getAndAdd(0x61c88647); static HashMap> map = new HashMap<>(); // 临界区上锁 synchronized static HashMap getMap() { Thread thread = Thread.currentThread(); if (!map.containsKey(thread)) { map.put(thread, new HashMap<>()); } return map.get(thread); } protected T initialValue() { return null; } public T get() { System.out.println("atomic: " + atomic); HashMap map = getMap(); if (!map.containsKey(this.threadLocalHash)) { map.put(this.threadLocalHash, this.initialValue()); } return (T) map.get(this.threadLocalHash); } public void set(T val) { HashMap map = getMap(); map.put(this.threadLocalHash, val); } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

package com.demo.threadlocal; public class TestMyThreadLocal { static MyThreadLocal threadLocal = new MyThreadLocal(){ @Override protected Long initialValue() { return Thread.currentThread().getId(); } }; public static void main(String[] args) { for (int i = 0; i < 100; i++) { new Thread(()->{ System.out.println(threadLocal.get()); }).start(); } } }

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

Java 任务调度 压力测试

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:[数据库] Navicat for Oracle基本用法图文介绍丨【绽放吧!数据库】
下一篇:为物流+智能 以科技见未来,华为云受邀参加2019全球物流技术大会
相关文章