Spring并发线程竞争

@RestController
public class HelloWorldController {
//高并发下多个线程修改进程中的变量
  static Integer num = 0;

  @RequestMapping("/stat")
  public Integer stat() {
    return num;
  }

  @RequestMapping("/add")
  public Integer add() {
    return num++;
  }
}

发100并发发一万次

 ab -n 10000 -c 100 http://localhost:8080/add

使用node.js 压测

const http = require('http');


const hostname = '127.0.0.1';
const port = 8080;
var num = 0;
const server = http.createServer((req, res) => {
  
    res.statusCode = 200;
    res.setHeader('Content-Type', 'text/plain');
    if (req.url === "/stat") {
        res.end(num +"");
    } else if ((req.url === "/add")) {
        num = num + 1;
        res.end(num +"");
    } else {
        res.end('Hello World');
    }

});

server.listen(port, hostname, () => {
    console.log(`Server running at http://``{hostname}:``{port}/`);
});

因为node.js是单线程压测出来的结果是正确✅的

用锁变巨慢很危险

@RestController
public class HelloWorldController {
  static Integer num = 0;

  synchronized void __add() throws InterruptedException {
    Thread.sleep(100);//模拟数据库io耗时
    num++;

  }
  @RequestMapping("/stat")
  public Integer stat() {
    return num;
  }


    @RequestMapping("/add")
    public Integer add() throws InterruptedException {
        __add();
        return 1;
    }

}

##用 ThreadLocal改造

@RestController
public class HelloWorldController {

  static ThreadLocal<Integer> num =
      new ThreadLocal<Integer>() {
        @Override
        protected Integer initialValue() {
          return 0;
        }
      };

  void __add() throws InterruptedException {
    Thread.sleep(100);
    int sum = num.get() + 1;
    num.set(sum);
  }

  @RequestMapping("/stat")
  public Integer stat() {
    return num.get();
  }

  @RequestMapping("/add")
  public Integer add() throws InterruptedException {
    __add();
    return 1;
  }
}

ThreadLocal

你会发现每个值分散在很多线程ThreadLocal中,怎么把所有线程计算结果收集起来?


class Var<T> {
  T v;

  public void set(T value) {
    v = value;
  }

  public T get() {
    return v;
  }
}

@RestController
public class HelloWorldController {

  // Integer 是值类型必须传引用类型
  // static HashMap<String, Integer> map = new HashMap<>();
  public static Set<Var<Integer>> set = new HashSet<>();

  static ThreadLocal<Var<Integer>> varThreadLocal =
      new ThreadLocal<Var<Integer>>() {
        @Override
        protected Var<Integer> initialValue() {

          Var<Integer> v = new Var<>();
          v.set(0);
          addSet(v);//这里存在临界区所有要加锁 
          return v;
        }
      };

  static synchronized void addSet(Var<Integer> v) {
    set.add(v);
  }

  void __add() throws InterruptedException {
    Thread.sleep(100);
    Var<Integer> integerVar = varThreadLocal.get();
    integerVar.set(integerVar.get() + 1);
  }

  @RequestMapping("/stat")
  public Integer stat() {
    if (set.isEmpty()) {
      return 0;
    }

    return set.stream().filter(Objects::nonNull).map(x -> x.get()).reduce((a, x) -> a + x).get();
  }

  @RequestMapping("/add")
  public Integer add() throws InterruptedException {
    __add();
    return 1;
  }

ThreadLoca