关键源码

这是我的第一次并发模拟的演示例子,是使用SpringBoot搭建演示环境。 虽然很简单,但是万丈高楼平地起,基础得打好~之后我会更加详实地补充高级内容!

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
//为输出方便,加log注解
@Slf4j
@NotThreadSafe //该自定义注解只是标示其为非线程安全类
public class ConcurrencyTest {

//请求总数量
public static int clientTotal = 5000;
//并发数量
public static int threadTotal = 200;
//用来计数
public static int count = 0;

public static void main(String[] args) {
//定义线程池
ExecutorService executorService = Executors.newCachedThreadPool();
//定义信号量,参数为并发数量
final Semaphore semaphore = new Semaphore(threadTotal);
//参数为请求总数
final CountDownLatch countDownLatch = new CountDownLatch(clientTotal);
for (int i = 0; i < clientTotal; i++) {
executorService.execute(() -> {
//核心方法执行前后需获取/释放信号量
//由于使用信号量可能会因执行中断而产生异常
//只有当semaphore.acquire()执行完(根据目前的并发数判断该线程是否允许被执行,否则会被阻塞)有返回效果,线程才会执行其核心方法
try {
semaphore.acquire();
add();
semaphore.release();
} catch (InterruptedException e) {
// e.printStackTrace();
log.error("中断异常InterruptedException:", e);
}
//执行闭锁,进行请求总数减一
countDownLatch.countDown();
});
}
//调用countDownLatch.await()方法保证目前已经**减为零**。
try {
countDownLatch.await();
//闭锁执行完毕,关闭线程池
executorService.shutdown();
log.info("计数count:{}",count);
} catch (InterruptedException e) {
// e.printStackTrace();
log.error("计数产生中断异常InterruptedException:", e);
}
}

//计数方法
private static void add() {
count++;
}
}
第一次运行结果

图

第二次运行结果

图

第三次运行结果

图

根据三次结果,显然该类是非线程安全的类。 所以其前面标注上之前定义的注解

并发模拟之工具

Postman:Http请求模拟工具。

在接口测试中是一个十分给力的工具;但在并发模拟方面做得不够专业。

Apache Bench (AB):Apache附带的工具,用以测试网站性能。

AB小巧简单,上手容易,但是没有图形化界面,也无法做到实时监控。

JMeter:Apache组织开发的压力测试工具。

下载安装并配置环境变量。


并发模拟之代码

CountDownLatch类

其原理图如下:

图示+解释

根据上图:其他线程每次执行结束即执行一次countDown(),已经执行结束任务的线程执行await()判断当前计数cnt是否为零,未至0则等待其他线程,只当计数cnt为0时该线程会被resumed去执行后续的操作。

该类可以通过计数来阻塞线程,主要用于线程执行完之后的其他处理:指定一个计数值,在并发环境下由线程进行减1操作,当计数值变为0之后,被await方法阻塞的线程将会唤醒,实现线程间的同步。通俗理解:已经执行完成的线程等待还未完成的线程,最后所有线程一同执行另外的任务。

Semphore类

Semphore从字面上理解就是信号量,学过《操作系统》课程的人应该知道,在操作系统调度任务时,根据其设定的Semphore值进行同时运行的线程数量的操作。
同理,此处的Semphore的含义也是如此。它主要是用来控制同时并发的线程数。

该两个类通常会结合线程池ThreadPool进行并发的模拟。

自定义的注解

1
2
3
4
5
6
7
8
//声明注解需两个属性。此ThreadSafe只是一个**线程安全**的标识
@Target(ElementType.TYPE)//给类做注解,其中target的其他值很多
@Retention(RetentionPolicy.SOURCE)//只在源码中起作用:标识。其他值详述。
public @interface ThreadSafe {

//通过value()给其名字,并给出默认名称。
String value() default "";
}

截图

@Target源码
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
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
/**
* The constants of this enumerated type provide a simple classification of the
* syntactic locations where annotations may appear in a Java program. These
* constants are used in {@link Target java.lang.annotation.Target}
* meta-annotations to specify where it is legal to write annotations of a
* given type.
*
* <p>The syntactic locations where annotations may appear are split into
* <em>declaration contexts</em> , where annotations apply to declarations, and
* <em>type contexts</em> , where annotations apply to types used in
* declarations and expressions.
*
* <p>The constants {@link #ANNOTATION_TYPE} , {@link #CONSTRUCTOR} , {@link
* #FIELD} , {@link #LOCAL_VARIABLE} , {@link #METHOD} , {@link #PACKAGE} ,
* {@link #PARAMETER} , {@link #TYPE} , and {@link #TYPE_PARAMETER} correspond
* to the declaration contexts in JLS 9.6.4.1.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.FIELD)} may only be written as a modifier for a
* field declaration.
*
* <p>The constant {@link #TYPE_USE} corresponds to the 15 type contexts in JLS
* 4.11, as well as to two declaration contexts: type declarations (including
* annotation type declarations) and type parameter declarations.
*
* <p>For example, an annotation whose type is meta-annotated with
* {@code @Target(ElementType.TYPE_USE)} may be written on the type of a field
* (or within the type of the field, if it is a nested, parameterized, or array
* type), and may also appear as a modifier for, say, a class declaration.
*
* <p>The {@code TYPE_USE} constant includes type declarations and type
* parameter declarations as a convenience for designers of type checkers which
* give semantics to annotation types. For example, if the annotation type
* {@code NonNull} is meta-annotated with
* {@code @Target(ElementType.TYPE_USE)}, then {@code @NonNull}
* {@code class C {...}} could be treated by a type checker as indicating that
* all variables of class {@code C} are non-null, while still allowing
* variables of other classes to be non-null or not non-null based on whether
* {@code @NonNull} appears at the variable's declaration.
*
* @author Joshua Bloch
* @since 1.5
* @jls 9.6.4.1 @Target
* @jls 4.1 The Kinds of Types and Values
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,

/** Field declaration (includes enum constants) */
FIELD,

/** Method declaration */
METHOD,

/** Formal parameter declaration */
PARAMETER,

/** Constructor declaration */
CONSTRUCTOR,

/** Local variable declaration */
LOCAL_VARIABLE,

/** Annotation type declaration */
ANNOTATION_TYPE,

/** Package declaration */
PACKAGE,

/**
* Type parameter declaration
*
* @since 1.8
*/
TYPE_PARAMETER,

/**
* Use of a type
*
* @since 1.8
*/
TYPE_USE
}
解读源码:
@Retention源码
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
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,

/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,

/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
解读源码:

此处为坑,后续会补上!!!