首页 锁(Java)性能探究
文章
取消

锁(Java)性能探究

Welcome

出于对 Java 锁的性能好奇,决定对锁使用的多种场景进行性能测试研究。 其中包括 synchronizedStampedLockReentrantLock 情况的研究。

1. 测试环境

环境备注
JDK版本 8
OSmacOS Ventura 13.1
CPUM1 Pro
内存32G
磁盘APPLE SSD AP0512R

2. 场景说明

锁方式对应方法
synchronized 方法锁syncByMethod
synchronized 常量锁syncByConstant
synchronized 对象锁syncByThis
StampedLock 写锁stampedLock
ReentrantLockreentrantLock

3. 结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
  // syncByMethod 方式, 共执行 20组,每组 100次,最长耗时 2540毫秒, 最短耗时 128毫秒, 平均耗时 1414.950000毫秒
  // syncByMethod 方式, 共执行 20组,每组 1000次,最长耗时 25461毫秒, 最短耗时 3702毫秒, 平均耗时 19282.150000毫秒

  // syncByThis 方式, 共执行 20组,每组 100次,最长耗时 2564毫秒, 最短耗时 127毫秒, 平均耗时 1542.350000毫秒
  // syncByThis 方式, 共执行 20组,每组 1000次,最长耗时 25560毫秒, 最短耗时 3598毫秒, 平均耗时 19210.350000毫秒

  // syncByConstant 方式, 共执行 20组,每组 100次,最长耗时 2541毫秒, 最短耗时 128毫秒, 平均耗时 1485.900000毫秒
  // syncByConstant 方式, 共执行 20组,每组 1000次,最长耗时 25605毫秒, 最短耗时 14804毫秒, 平均耗时 21494.650000毫秒

  // stampedLock 方式, 共执行 20组,每组 100次,最长耗时 2541毫秒, 最短耗时 2419毫秒, 平均耗时 2497.800000毫秒
  // stampedLock 方式, 共执行 20组,每组 1000次,最长耗时 25272毫秒, 最短耗时 25085毫秒, 平均耗时 25215.000000毫秒

  // reentrantLock 方式, 共执行 20组,每组 100次,最长耗时 2550毫秒, 最短耗时 128毫秒, 平均耗时 1479.400000毫秒
  // reentrantLock 方式, 共执行 20组,每组 1000次,最长耗时 25592毫秒, 最短耗时 1362毫秒, 平均耗时 16253.050000毫秒

总结: 在普通场景下使用,各种锁的性能相差无几。

4. 代码

4.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
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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
package priv.explore8.demo;

import lombok.Getter;
import lombok.Setter;
import lombok.SneakyThrows;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;

/**
 * 锁的性能测试
 * <pre>
 *    总结:synchronized经过不断的优化,到目前JDK8版本已经不再是低级笨重的锁了,
 *    在一起其他场合性能已经优于reentrantLock和stampedLock了
 *
 *    原因:synchronized引入了偏向锁、轻量级锁、重量级锁,并提供了自动的升级降级机制
 * </pre>
 *
 * @author Oriental Ming
 * @date 2023/5/6 16:35
 */
public class LockPerformanceDemo {


    /**
     * 模拟争抢的资源
     */
    @Getter
    private long resources = 0;
    private static final Object LOCK_KEY = new Object();

    @Setter
    private StampedLock lock;
    @Setter
    private ReentrantLock reentrantLock;

    /**
     * synchronized 方法锁
     * 方法锁锁定的该方法所属对象隐藏字段的monitor
     */
    @SneakyThrows
    public synchronized void syncByMethod() {
        TimeUnit.MILLISECONDS.sleep(1);
        resources++;
    }

    /**
     * synchronized this 对象锁
     */
    @SneakyThrows
    public void syncByThis() {
        synchronized (this) {
            TimeUnit.MILLISECONDS.sleep(1);
            resources++;
        }
    }

    /**
     * synchronized 对象锁,锁内容
     */
    @SneakyThrows
    public void syncByConstant() {
        synchronized (LOCK_KEY) {
            TimeUnit.MILLISECONDS.sleep(1);
            resources++;
        }
    }

    /**
     * JDK8 StampedLock锁
     */
    @SneakyThrows
    public void stampedLock() {
        Lock writeLock = lock.asWriteLock();
        try {
            writeLock.lock();

            TimeUnit.MILLISECONDS.sleep(1);
            resources++;
        } finally {
            writeLock.unlock();
        }
    }

    /**
     * ReentrantLock 锁
     */
    @SneakyThrows
    public void reentrantLock() {
        try {
            reentrantLock.lock();

            TimeUnit.MILLISECONDS.sleep(1);
            resources++;
        } finally {
            reentrantLock.unlock();
        }
    }

}

4.2 测试源代码

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
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package priv.explore8.demo;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import cn.hutool.core.thread.ThreadUtil;
import com.google.common.collect.Lists;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.CsvSource;

import java.util.List;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.StampedLock;
import java.util.stream.Collectors;

import static org.junit.jupiter.api.Assertions.assertEquals;

class LockPerformanceDemoTest {

    private LockPerformanceDemo service;
    /**
     * 运行多少组
     */
    public int groupNumber;
    /**
     * 每组运行的次数
     */
    public int perGroupNumber;
    public List<Long> timeCountList;

    @BeforeEach
    void setUp() {
        service = new LockPerformanceDemo();
    }

    @AfterEach
    void afterEach() {
        assertEquals((long) groupNumber * perGroupNumber, service.getResources());
    }

    @ParameterizedTest
    @CsvSource({"20, 100", "20, 1000"})
    void syncByMethod(int groupCount, int perGroupCount) {
        groupNumber = groupCount;
        perGroupNumber = perGroupCount;
        timeCountList = Lists.newArrayListWithCapacity(groupNumber * perGroupCount);

        reduce("syncByMethod", service::syncByMethod);
        // syncByMethod 方式, 共执行 20组,每组 100次,最长耗时 2540毫秒, 最短耗时 128毫秒, 平均耗时 1414.950000毫秒
        // syncByMethod 方式, 共执行 20组,每组 1000次,最长耗时 25461毫秒, 最短耗时 3702毫秒, 平均耗时 19282.150000毫秒
    }

    @ParameterizedTest
    @CsvSource({"20, 100", "20, 1000"})
    void syncByThis(int groupCount, int perGroupCount) {
        groupNumber = groupCount;
        perGroupNumber = perGroupCount;
        timeCountList = Lists.newArrayListWithCapacity(groupNumber * perGroupCount);

        reduce("syncByThis", service::syncByThis);
        // syncByThis 方式, 共执行 20组,每组 100次,最长耗时 2564毫秒, 最短耗时 127毫秒, 平均耗时 1542.350000毫秒
        // syncByThis 方式, 共执行 20组,每组 1000次,最长耗时 25560毫秒, 最短耗时 3598毫秒, 平均耗时 19210.350000毫秒
    }

    @ParameterizedTest
    @CsvSource({"20, 100", "20, 1000"})
    void syncByConstant(int groupCount, int perGroupCount) {
        groupNumber = groupCount;
        perGroupNumber = perGroupCount;
        timeCountList = Lists.newArrayListWithCapacity(groupNumber * perGroupCount);

        reduce("syncByConstant", service::syncByConstant);
        // syncByConstant 方式, 共执行 20组,每组 100次,最长耗时 2541毫秒, 最短耗时 128毫秒, 平均耗时 1485.900000毫秒
        // syncByConstant 方式, 共执行 20组,每组 1000次,最长耗时 25605毫秒, 最短耗时 14804毫秒, 平均耗时 21494.650000毫秒
    }

    @ParameterizedTest
    @CsvSource({"20, 100", "20, 1000"})
    void stampedLock(int groupCount, int perGroupCount) {
        groupNumber = groupCount;
        perGroupNumber = perGroupCount;
        timeCountList = Lists.newArrayListWithCapacity(groupNumber * perGroupCount);

        service.setLock(new StampedLock());

        reduce("stampedLock", service::stampedLock);
        // stampedLock 方式, 共执行 20组,每组 100次,最长耗时 2541毫秒, 最短耗时 2419毫秒, 平均耗时 2497.800000毫秒
        // stampedLock 方式, 共执行 20组,每组 1000次,最长耗时 25272毫秒, 最短耗时 25085毫秒, 平均耗时 25215.000000毫秒
    }

    @ParameterizedTest
    @CsvSource({"20, 100", "20, 1000"})
    void reentrantLock(int groupCount, int perGroupCount) {
        groupNumber = groupCount;
        perGroupNumber = perGroupCount;
        timeCountList = Lists.newArrayListWithCapacity(groupNumber * perGroupCount);

        service.setReentrantLock(new ReentrantLock());
        reduce("reentrantLock", service::reentrantLock);

        // reentrantLock 方式, 共执行 20组,每组 100次,最长耗时 2550毫秒, 最短耗时 128毫秒, 平均耗时 1479.400000毫秒
        // reentrantLock 方式, 共执行 20组,每组 1000次,最长耗时 25592毫秒, 最短耗时 1362毫秒, 平均耗时 16253.050000毫秒
    }

    /**
     * 指定任务,并统计运行的时长
     *
     * @param taskName 任务名称
     * @param task     任务
     */
    private void reduce(String taskName, Runnable task) {

        // 对 runAndRecordTime 执行 groupNumber 次
        ThreadUtil.concurrencyTest(groupNumber, () -> runAndRecordTime(taskName, task));

        // @formatter:off
        System.err.printf("%s 方式, 共执行 %d组,每组 %d次,最长耗时 %d毫秒, 最短耗时 %d毫秒, 平均耗时 %f毫秒 \n",
            taskName, groupNumber, perGroupNumber, CollUtil.max(timeCountList), CollUtil.min(timeCountList),
            timeCountList.stream().collect(Collectors.averagingLong(Long::longValue)));
        // @formatter:on
    }

    /**
     * 指定任务,并运行。记录每组执行的耗时时长
     *
     * @param taskName 任务名称
     * @param task     任务
     */
    private void runAndRecordTime(String taskName, Runnable task) {
        TimeInterval timer = DateUtil.timer();
        for (int i = 0; i < perGroupNumber; i++) {
            task.run();
        }

        long time = timer.intervalMs();
        timeCountList.add(time);
        System.out.printf("%s 方式执行 %d次, 用时 %d 毫秒 \n", taskName, perGroupNumber, time);
    }

}

完结撒花 😂 ! 制作不易,如引用原文,必须附此原文链接,否则违者必究!😈


本文由作者按照 CC BY 4.0 进行授权