Spring Boot学习笔记五--ThreadLocal实战

  • 我的技术博客:https://nezha.github.io,https://nezhaxiaozi.coding.me
  • 我的地址:https://www.jianshu.com/u/a5153fbb0434

本文的代码地址:GitHub ThreadLocal Demo

1. UserService

package com.nezha.learn.demo.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.concurrent.locks.ReentrantLock;

@Service
@Slf4j
public class UserService {
    private static final ThreadLocal numbers = new ThreadLocal<>();
    private Long normal = new Long(1);
    private ReentrantLock reentrantLock = new ReentrantLock();


    /**
     * 模拟高并发场景下的请求,观察ThreadLocal是否线程安全
     * @param number
     */
    public void getNumberSyncRest(int number) {
        Long id = Thread.currentThread().getId();
        log.info(">>>>>Thread ID:{},input number:{}", id, number);
        numbers.set(id);
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        log.info(">>>>>Thread ID:{},ThreadLocal content:{},input number:{}", id, numbers.get(), number);
    }

    /**
     * 观察高并发场景下,普通共享变量是否线程安全
     * @param number
     */
    public void getNumberSyncRestNormal(int number) {
        Long id = Thread.currentThread().getId();
        log.info(">>>>>Thread ID:{},input number:{}", id, number);
        normal = id;
        try {
            Thread.sleep(10);
        } catch (InterruptedException e) {

        }
        log.info(">>>>>Thread ID:{},Normal content:{},input number:{}", id, normal, number);
    }


    /**
     * 异步状态下测试ThreadLocal和普通变量的线程安全情况
     */
    @Async
    public void getNumberAsync() {
        Long id = Thread.currentThread().getId();
        log.info(">>>>Thread ID:{}", id);
        numbers.set(id);
        normal = id;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        log.info(">>>>Thread ID:{},ThreadLocal content:{},normal:{}", id, numbers.get(), normal);
    }

    /**
     * 测试可重入锁的并发安全性
     */
    @Async
    public void getNumberAsyncReeLock() {
        reentrantLock.lock();
        Long id = Thread.currentThread().getId();
        log.info(">>>>>Thread ID:{}", id);
        numbers.set(id);
        normal = id;
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {

        }
        log.info(">>>>>Thread ID:{},ThreadLocal content:{},normal:{}", id, numbers.get(), normal);
        reentrantLock.unlock();
    }

    /**
     * 测试synchronized锁的线程安全性
     */
    @Async
    public void getNumberAsyncSyncLock() {
        synchronized (normal){
            Long id = Thread.currentThread().getId();
            log.info(">>>>>Thread ID:{}", id);
            numbers.set(id);
            normal = id;
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {

            }
            log.info(">>>>>Thread ID:{},ThreadLocal content:{},normal:{}", id, numbers.get(), normal);
        }
    }
}

2. UserApi

package com.nezha.learn.demo.controller;

import com.nezha.learn.demo.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class UserApi {
    @Autowired
    private UserService userService;

    @GetMapping(value="/get/{id}")
    public int printNumber(@PathVariable int id) {
        // url的id可通过@PathVariable绑定到参数中
        userService.getNumberSyncRest(id);
        return 200;
    }

    @GetMapping(value="/getNorm/{id}")
    public int printNormalNumber(@PathVariable int id) {
        // url的id可通过@PathVariable绑定到参数中
        userService.getNumberSyncRestNormal(id);
        return 200;
    }
}

3. 测试类 UserServiceTest

package com.nezha.learn.demo.service;

import lombok.extern.slf4j.Slf4j;
import org.databene.contiperf.PerfTest;
import org.databene.contiperf.junit.ContiPerfRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;

import java.util.Random;

@RunWith(SpringRunner.class)
@SpringBootTest
@Slf4j
@EnableAsync
@AutoConfigureMockMvc
public class UserServiceTest {
    @Autowired
    private UserService userService;

    @Autowired
    private MockMvc mockMvc;

    private Random random = new Random();

    @Rule
    public ContiPerfRule contiPerfRule = new ContiPerfRule();

    @Test
    public void getNumber() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            userService.getNumberAsync();
            log.info("第{}个任务", i);
        }
        Thread.sleep(10000);
    }

    @Test
    public void getNumberAsyncSyncLock() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            userService.getNumberAsyncSyncLock();
            log.info("第{}个任务", i);
        }
        Thread.sleep(10000);
    }

    @Test
    public void getNumberAsyncReeLock() throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            userService.getNumberAsyncReeLock();
            log.info("第{}个任务", i);
        }
        Thread.sleep(10000);
    }

    @Test
    //10个线程 执行10次
    @PerfTest(invocations = 10, threads = 10)
    public void getNumberSyncRest() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/get/" + random.nextInt(20)));
    }

    @Test
    //10个线程 执行10次
    @PerfTest(invocations = 10, threads = 10)
    public void getNumberSyncRestNormal() throws Exception {
        mockMvc.perform(MockMvcRequestBuilders.get("/getNorm/" + random.nextInt(20)));
    }
}

其中测试类中多线程,并发量的测试我用的工具是:contiperf


    org.databene
    contiperf
    2.3.4
    test

你可能感兴趣的:(Spring Boot学习笔记五--ThreadLocal实战)