import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import java.util.UUID; @RestController @RequestMapping("/") @SpringBootApplication public class DemoApplication { public static void main(String[] args) { SpringApplication.run(DemoApplication.class, args); } @GetMapping("/hello") private String sayHello() throws InterruptedException { String id = UUID.randomUUID().toString(); System.out.println("进入 1: " + id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId()); // 模拟耗时操作 Thread.sleep(5000); System.out.println("进入 2: " + id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId()); return "hello2"; } }
ab -c 5 -n 5 http://localhost:8080/hello
模拟 5 个并发请求,输出如下:进入 1: 26ae2ea9-7e5c-4bd7-bc2d-8c4d0907d18a 时间: 1566302327095 线程 id: 28 进入 2: 26ae2ea9-7e5c-4bd7-bc2d-8c4d0907d18a 时间: 1566302332099 线程 id: 28 进入 1: 82768b68-3a05-4768-8290-8b43a176a57e 时间: 1566302332125 线程 id: 30 进入 1: 05337b19-9a5a-41e9-8c9b-944d8ceeb06c 时间: 1566302332125 线程 id: 29 进入 1: 3b7eea0a-4d8c-4878-9e21-741f1e6d2e76 时间: 1566302332125 线程 id: 32 进入 1: 587305cd-89fc-441d-a575-ab436a63ad40 时间: 1566302332125 线程 id: 31 进入 2: 3b7eea0a-4d8c-4878-9e21-741f1e6d2e76 时间: 1566302337127 线程 id: 32 进入 2: 05337b19-9a5a-41e9-8c9b-944d8ceeb06c 时间: 1566302337127 线程 id: 29 进入 2: 587305cd-89fc-441d-a575-ab436a63ad40 时间: 1566302337127 线程 id: 31 进入 2: 82768b68-3a05-4768-8290-8b43a176a57e 时间: 1566302337127 线程 id: 30
进入 1 进入 1 进入 1 进入 1 进入 1 进入 2 进入 2 进入 2 进入 2 进入 2
![]() | 1 hiw2016 OP 我应该换到 Java 节点么 |
![]() | 3 BCy66drFCvk1Ou87 2019-08-20 21:28:37 +08:00 spring 中的 controller 是非线程安全的,多个线程访问会导致意料不到的结果 |
![]() | 4 hiw2016 OP @HuasLeung 感谢你的回复~ 我重复尝试过多次,总是第一次的请求完整处理完,才开始处理后续请求,而后续请求是可以多线程进入方法并打印出「进入 1 」。疑问在于为什么第一次不能多线程执行方法呢? |
![]() | 5 BCy66drFCvk1Ou87 2019-08-20 21:44:07 +08:00 ![]() @hiw2016 ```` // 模拟耗时操作 Thread.sleep(5000); ```` sleep()方法是不会释放锁的。在设置的 5 秒钟内不清楚你的请求的时间间隔是怎么样的,只要你的第 2 次请求在 5 秒后请求都能得到“总是第一次的请求完整处理完” |
![]() | 6 BCy66drFCvk1Ou87 2019-08-20 21:44:57 +08:00 @HuasLeung 打错,5 秒钟内 |
![]() | 7 limuyan44 2019-08-20 21:56:29 +08:00 ![]() 不应该出现这种情况的,把 UUID.randomUUID()去掉试看看呢。 |
![]() | 8 BCy66drFCvk1Ou87 2019-08-20 22:09:27 +08:00 ```` public class DemoApplication { volatile String id = UUID.randomUUID().toString(); } ```` |
![]() | 9 BCy66drFCvk1Ou87 2019-08-20 22:12:24 +08:00 @HuasLeung ```` public class DemoApplication { private volatile String id = UUID.randomUUID().toString(); //省略 @GetMapping("/hello") private String sayHello() throws InterruptedException { System.out.println("进入 1: " + this.id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId()); // 模拟耗时操作 Thread.sleep(5000); System.out.println("进入 2: " + this.id + " 时间: " + System.currentTimeMillis() + " 线程 id: " + Thread.currentThread().getId()); return "hello2"; } } ```` 手机写代码真是一点不方便,没编辑完就发出去了……试试看,这样写应该能解决单例模式的线程不安全问题 |
![]() | 10 gIrl1990 2019-08-20 22:12:29 +08:00 ab 是啥工具 |
&bsp; 11 iffi 2019-08-20 22:25:10 +08:00 没明白你想问什么 |
13 notreami 2019-08-20 22:31:01 +08:00 问题的关键是,你以为设置 500ms 休眠就真的精准 500ms ??? |
14 notreami 2019-08-20 22:36:16 +08:00 ![]() 额,不对。看错了。你这是认为,为啥第一次请求结束了才有第二次请求。 |
![]() | 15 anzu 2019-08-20 22:46:29 +08:00 为什么会有这种预期?如果一直每秒都有一个请求进来,那不是永远都无法进入 2 阶段了? |
![]() | 16 hiw2016 OP @HuasLeung 感谢,我换了一种测试方式,手动在五个不同的终端中 curl,输出是我的预期了。那可能是我理解错了 ab 的机制。 |
![]() | 17 hiw2016 OP @limuyan44 也是一样的,但是我换了一种测试方法,手动在五个不同的终端中 curl,输出是我的预期了。可能是我理解错了 ab 的机制。 |