媒介
节制并发的要领许多,从最基本的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框架,导致了写充血模子的措施员变多,不留意并发的话,就会呈现问题。下面我们来看看详细的业务场景。
业务场景
业务场景阐明
第一个场景,互联网如此浩瀚的用户修改小我私家书息,这算不算并发?谜底是:算也不算。