莫大人

并发问题

最近遇到一个棘手的问题,说棘手一方面之前没考虑过,另一方面其中的业务逻辑实在太难看懂了。

以后写代码希望以此为戒吧

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去。不过很可能处理时间很长,这也是没办法的。

 

posted on 2017-03-14 18:14  莫大人  阅读(145)  评论(0编辑  收藏  举报

导航