并发问题
最近遇到一个棘手的问题,说棘手一方面之前没考虑过,另一方面其中的业务逻辑实在太难看懂了。
以后写代码希望以此为戒吧
1.嵌套层数不要超过3层。
2.逻辑上超过3层的if-else代码可以使用卫语句,或者状态模式来实现。
3.对于高并发业务需要考虑多线程并发问题,互斥锁,死锁等问题。
问题描述:
1.前端可以触发,根据不同的参数获取不同的处理数据。
2.后端有自动任务获取数据,并处理数据。(创建线程去处理)
逻辑主要分3步
a.获取数据
b.处理数据(处理时间还有点长)
c.更新数据
模拟分析:
1.多线程,多实例处理
本人待的项目是很老的项目,以此为背景。service,dao为原型模式,对于数据的模拟采用饥汉式单例模式。
Data.java
package com.midea.test; public class Data { private static Data data = new Data(); private volatile int num = 10; private Data(){ } public static Data getInstance() { if (data == null) { return new Data(); } return data; } public int getNum() { return num; } public void setNum(int num) { this.num = num; } }
ProductService
package com.midea.test; public class ProductService { private ProductDao productDao; public ProductDao getProductDao() { return productDao; } public void setProductDao(ProductDao productDao) { this.productDao = productDao; } public void process() { int n = productDao.getNum(); System.out.println(Thread.currentThread().getId()+",before:"+n); productDao.setNum(n+1); System.out.println(Thread.currentThread().getId()+",after:"+productDao.getNum()); } }
ProductDao
package com.midea.test; public class ProductDao { public int getNum() { Data data = Data.getInstance(); return data.getNum(); } public void setNum(int num) { Data data = Data.getInstance(); data.setNum(num); } }
测试
@Test public void test1(){ for (int i=0; i<10; i++) { Thread t = new Thread(){ @Override public void run() { ProductService service = new ProductService(); System.out.println(service.hashCode()); ProductDao dao = new ProductDao(); service.setProductDao(dao); service.process(); } }; t.start(); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); }
System.out.println("最后结果:"+Data.getInstance().getNum());
}
查看结果:
service的hashcode不一样,每个service都是一个new的。每个线程都是各自用自己的service去处理的。
本意是数据 10自增10次,然后得到20,实际结果却不是20
380254118 1318663487 745988969 827070797 2064721795 97255069 330889316 13,before:10 13,after:11 14,before:10 14,after:11 16,before:10 16,after:11 10,before:11 10,after:12 12,before:10 12,after:11 15,before:11 15,after:12 11,before:10 11,after:11 1421571929 1603837828 19,before:11 19,after:12 18,before:12 487638052 17,before:12 17,after:13 18,after:13
用于我们业务上是要求
a.获取数据
b.处理数据(处理时间还有点长)
c.更新数据
获取到数据之后是要用来处理的。这就要求,我获取到的数据是最新的,且在我处理完之前是不会别的人改变的。
这就要求了整个业务过程的互斥。所以我在这里将service改成单例,处理方法添加互斥锁。
service
package com.midea.test; public class ProductService { private static ProductService productService = new ProductService(); private ProductService() { } public static ProductService getInstance() { if (productService == null) { return new ProductService(); } return productService; } private ProductDao productDao; public ProductDao getProductDao() { return productDao; } public void setProductDao(ProductDao productDao) { this.productDao = productDao; } public synchronized void process() { int n = productDao.getNum(); System.out.println(Thread.currentThread().getId()+",before:"+n); productDao.setNum(n+1); System.out.println(Thread.currentThread().getId()+",after:"+productDao.getNum()); } }
测试方法
for (int i=0; i<10; i++) { Thread t = new Thread(){ @Override public void run() { ProductService service = ProductService.getInstance(); System.out.println(service.hashCode()); ProductDao dao = new ProductDao(); service.setProductDao(dao); service.process(); } }; t.start(); } try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("最后结果:"+Data.getInstance().getNum());
结果:
发现service的hashcode一致,同一个service实例,最后结果为20,与预期一致。
19627754
19627754
19627754
19627754
19627754
11,before:10
11,after:11
10,before:11
10,after:12
14,before:12
14,after:13
12,before:13
12,after:14
13,before:14
13,after:15
19627754
19627754
19,before:15
19,after:16
19627754
17,before:16
17,after:17
18,before:17
18,after:18
19627754
19627754
15,before:18
15,after:19
16,before:19
16,after:20
最后结果:20
嗯,准备按这个思路修改bug去。不过很可能处理时间很长,这也是没办法的。