欢迎访问昆山宝鼎软件有限公司网站! 设为首页 | 网站地图 | XML | RSS订阅 | 宝鼎邮箱 | 后台管理


新闻资讯

MENU

软件开发知识

什么是并发 由一个大家都了解的例子引入我们今天的主题:并发 类共享变量遭遇并发 public class Demo {

点击: 次  来源:昆山软开发 时间:2017-11-10

原文出处: 徐靖峰

媒介

节制并发的要领许多,从最基本的synchronized,juc中的lock,到数据库的行级锁,乐观锁,灰心锁,再到中间件级此外redis,zookeeper漫衍式锁。出格是低级措施员,对付所谓的锁一直都是听的比用的多,第一篇文章不深入探讨并发,更多的是一个入门先容,适合于初学者,主题是“按照并发呈现的详细业务场景,利用公道的节制并发手段”。

什么是并发

由一个各人都相识的例子引入我们本日的主题:并发

类共享变量遭遇并发

public class Demo {
    public Integer count = 0;
    public static void main(String[] args) {
        final Demo demo = new Demo();
        Executor executor = Executors.newFixedThreadPool(10);
        for(int i=0;i<1000;i++){
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    demo.count++;
                }
            });
        }
        try {
            Thread.sleep(5000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("final count value:"+demo1.count);
    }
}

final count value:973

本例中建设了一个初始化时具有10个线程的线程池,多线程对类变量count举办自增操纵。这个进程中,自增操纵并不是线程安详的,happens-before原则并不会保障多个线程执行的先后顺序,导致了最终功效并不是想要的1000

下面,我们把并发中的共享资源从类变量转移到数据库中。

充血模子遭遇并发

@Component
public class Demo2 {
    @Autowired
    TestNumDao testNumDao;
    @Transactional
    public void test(){
        TestNum testNum = testNumDao.findOne("1");
        testNum.setCount(testNum.getCount()+1);
        testNumDao.save(testNum);
    }
}

依旧利用多线程,对数据库中的记录举办+1操纵

Demo2 demo2;

public String test(){
    Executor executor = Executors.newFixedThreadPool(10);
    for(int i=0;i<1000;i++){
        executor.execute(new Runnable() {
            @Override
            public void run() {
                demo2.test();
            }
        });
    }
    return "test";
}

数据库的记录

id	| count
1	| 344

初窥门径的措施员会认为事务最根基的ACID中便包括了原子性,可是事务的原子性和本日所讲的并发中的原子操纵仅仅是名词上有点雷同。而有点履历的措施员都能知道这中间产生了什么,这只是袒露了项目中并发问题的冰山一角,千万不要认为上面的代码没有须要罗列出来,我在实际项目开拓中,曾经见到有多年岁情履历的措施员仍然写出了雷同于上述会呈现并发问题的代码。

贫血模子遭遇并发

@RequestMapping("testSql")
    @ResponseBody
    public String testSql() throws InterruptedException {
        final CountDownLatch countDownLatch = new CountDownLatch(1000);
        long start = System.currentTimeMillis();
        Executor executor = Executors.newFixedThreadPool(10);
        for(int i=0;i<1000;i++){
            executor.execute(new Runnable() {
                @Override
                public void run() {
                    jdbcTemplate.execute("update test_num set count = count + 1 where id = '1'");
                    countDownLatch.countDown();
                }
            });
        }
        countDownLatch.await();
        long costTime =System.currentTimeMillis() - start;
        System.out.println("共耗费:"+costTime+" s");
        return "testSql";
    }

数据库功效: count : 1000 到达了预期结果
这个例子我顺便记录了耗时,节制台打印:共耗费:113 ms

简朴比拟一下二,三两个例子,都是想对数据库的count举办+1操纵,独一的区别就是,后者的+1计较产生在数据库,而前者的计较依赖于事先查出来的值,而且计较产生在措施的内存中。而此刻大部门的ORM框架,导致了写充血模子的措施员变多,不留意并发的话,就会呈现问题。下面我们来看看详细的业务场景。

业务场景

  • 修改小我私家书息
  • 修改商品信息
  • 扣除账户余额,扣减库存
  • 业务场景阐明

    第一个场景,互联网如此浩瀚的用户修改小我私家书息,这算不算并发?谜底是:算也不算。

  • 算,从措施员角度来看,每一个用户请求进来,都是挪用的同一个修改进口,详细一点,就是映射到controller层的同一个requestMapping,所以必然是并发的。
  • 不算,固然措施是并发的,可是从用户角度来阐明,每小我私家只可以修改本身的信息,所以,差异用户的操纵其实是断绝的,所以不算“并发”。这也是为什么许多开拓者,在日常开拓中一直不留意并发节制,却也没有产生太大问题的原因,大大都低级措施员开拓的还都是CRM,OA,CMS系统。