• Like
  • Save
并发控制
Upcoming SlideShare
Loading in...5
×
 

并发控制

on

  • 2,089 views

给核心测试团队分享的并发相关知识

给核心测试团队分享的并发相关知识

Statistics

Views

Total Views
2,089
Views on SlideShare
1,289
Embed Views
800

Actions

Likes
1
Downloads
24
Comments
1

4 Embeds 800

http://www.beralee.com 786
http://cache.baidu.com 12
http://webcache.googleusercontent.com 1
http://cache.baiducontent.com 1

Accessibility

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel

11 of 1

  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
  • I also write an introduction to concurrency:
    http://www.slideshare.net/leefs/effective-java-concurrency
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    并发控制 并发控制 Presentation Transcript

    • Concurrent Tips
      祢衡@alipaywww.beralee.com
    • 主要知识
      • 并发的原因
      • 找出并发问题
      • Java的并发控制
      • 数据库的并发控制
    • 为什么要并发?
      为了
      性能
    • 什么情况下会并发?
      几乎所有的场景
    • 应用服务器的架构
      • Http请求
      • WebService访问
      • ESB
      • Notify
      • Database
      ……
    • 并发带来了什么?
      • 硬件资源充分利用
      • 相对复杂的编程
      • 难以发现的潜在问题
      • 错误数据和结果
    • 并发问题的根本原因
      状态
    • 如何判断有状态?
      如果在不同的时间点使用相同的参数访问同一个方法,返回了不同的值,那么这个对象就是有状态的
    • 并发问题的根本原因
      所有的并发问题
      都是由状态引起的
    • 并发问题的根本原因
      准确的说,是
      不稳定的状态
    • Java对并发的控制
      • Synchronized
      • Java.util.concurrent.* 一系列的并发措施:
      • Lock
      • Atomic
      • BlockingQueue
      • ConcurrentHashMap
      ……
    • Synchronized
      理解锁的是谁?
    • 一个计数器
      publicclassUnSafeCounter{
      privateintvalue;
      publicintgetNext(){
      returnvalue++;
      }
      }
      非线程安全,value++不是原子操作,value状态不稳定,需要同步
    • publicclassSafeCounter{
      privateintvalue;
      publicsynchronizedintgetNext(){
      returnvalue++;
      }
      }
      线程安全,锁的对象是当前SafeCounter实例,不会影响到另一个SafeCounter实例的getNext方法
    • publicclassSafeCounter{
      privateintvalue;
      privatefinal Object lock=new Object();
      publicintgetNext(){
      synchronized(lock){
      returnvalue++;
      }
      }
      }
      线程安全,锁的对象是lock,可以灵活控制,只锁真正需要同步的代码,最佳实践。注意lock不能为null,也不能改变
    • publicclassSafeCounter{
      privateintvalue;
      publicintgetNext(){
      synchronized(SafeCounter.class){
      returnvalue++;
      }
      }
      }
      线程安全,锁的对象是SafeCounter类,任何实例都会受锁的影响,一般不这样用
    • 更好的Lock
      Lock lock=newReentrantLock();
      lock.lock();
      try{
      // update object state
      }
      finally{
      lock.unlock();
      }
      //尝试获得锁
      if(lock.tryLock()){
      System.out.println("获得锁了");
      }else{
      System.out.println("有人在用");
      }
      灵活控制锁竞争时的处理,拥有更好的性能
    • Atomic
      publicstaticAtomicLongatomicLong=newAtomicLong();
      publicstaticlonggetNext(){
      returnatomicLong.getAndIncrement();
      }
      最佳实践:使用原子操作类,非阻塞,获得最好的性能。
    • 并发异常
      publicclass Cache {
      publicstatic List<Object>cacheList=newArrayList<Object>();
      publicstatic Map<String,Object>cacheMap=newHashMap<String,Object>();
      }
      多线程操作时抛出java.util.ConcurrentModificationException异常,快速失败
    • 防止并发异常
      ArrayList----Vector
      Hashmap----HashTable
      Collections.synchronizedList(new ArrayList(...))
      Collections. synchronizedMap(new HashMap(...))
      synchronized (list){
      ……

      Vector HashTable性能低下
      Collections.synchronizedList更加危险,在负载重的情况下依然会抛出NP或并发异常
      现在已经木有人这么用了….
    • 并发设施
      publicclassSafeCache{
      publicstatic List<Object>cacheList=newCopyOnWriteArrayList<Object>();
      publicstaticConcurrentMap<String,Object>cacheMap=newConcurrentHashMap<String,Object>();
      }
      最佳实践:使用java.util.concurrent里的并发设施,它们本身是Lock Free的,从而获得最佳性能。
      ConcurrentLinkedQueue
      BlockingQueue ……
    • 并发辅助工具
      CountDownLatch
      CyclicBarrier
      Semaphore
      FutureTask
      ExecutorService
      ……
      最佳实践:使用java.util.concurrent里的并发控制工具,代替notifyAll,wait, join
    • CountDownLatch
      • 倒数到0时,释放所有等待的线程,否则阻塞。
      • countDown() 倒数
      • await() 阻塞等待
      • 所有线程启动后await等待,直到countDown到0时大家一起跑,模拟并发
      • 每条线程跑完时countDown,所有线程跑完后await释放,代替join
    • CountDownLatch
      finalint count = 10;
      finalCountDownLatchcdt=newCountDownLatch(count);
      for(int i = 0; i < 10;++i){
      Thread t =new Thread("test_thread"+i){
      publicvoid run(){
      // do sth.
      cdt.countDown();
      }
      };
      t.start();
      }
      cdt.await();
    • CyclicBarrier
      • 闸门,公共屏障点
      • await() 阻塞等待
      • 多个线程互相等待到彼此做完,再开始下一个任务,相当于集合点
    • barrier=newCyclicBarrier(threadCount,newRunnable(){
      publicvoid run(){
      collectTestResult();
      }
      });
      for(inti= 0;i<threadCount;++i){
      Thread thread=new Thread("test-thread "+i){
      publicvoid run(){
      try{
      doTest();
      barrier.await();
      }catch(InterruptedException e){
      return;
      }catch(BrokenBarrierException e){
      return;
      }
      }
      };
      thread.start();
      }
    • 示例
      • 充值
      • 提现
      • 转账
    • public class Account {
      privateMoney balance;
      publicvoid withdraw(Money amount) {
      balance = balance. subtract (amount);
      }
      publicvoid deposit(Money amount) {
      balance = balance. add(amount);
      }
      }
      非线程安全!
    • public class Account {
      privateMoney balance;
      public synchronized void withdraw(Money amount) {
      balance = balance.add(amount);
      }
      public synchronized void deposit(Money amount) {
      balance = balance.subtract(amount);
      }
      }
      线程安全!?
    • public class Account {

      publicvoidtransferTo(Account to , Money amount) {
      this.withdraw(amount);


      




 to.deposit(amount);
      }
      }
      线程安全!?
    • public class Account {

      public synchronized voidtransferTo(Account to , Money amount) {
      this.withdraw(amount);


      




 to.deposit(amount);
      }
      }
      OK了!?
    • Account accountA
      Account accountB
      ….
      accountA.transferTo(accountB,new Money(100))
      accountB.transferTo(accountA,new Money(100))
      死锁了…
    • 一个工具类
      publicstatic final SimpleDateFormatformat=newSimpleDateFormat("HH:mm:ss");
      /**
      * 将字符串转换成日期类
      * @paramdateStr需要转换的字符串例如 12:00:00
      * @return日期对象
      * @throwsParseException字符串不符合日期格式
      */
      publicstatic Date formatStrToDate(String dateStr)throwsParseException{
      returnformat.parse(dateStr);
      }
    • 注意看注释
      SimpleDateFormat:
      …..
      Synchronization
      Date formats are not synchronized. It is recommended to create separate format instances for each thread. If multiple threads access a format concurrently, it must be synchronized externally.
      Money:
      …..
      BigDecimal是Immutable,一旦创建就不可更改,对BigDecimal进行任意运算都会生成一个新的BigDecimal对象,因此对于大批量统计的性能不够满意。Money类是mutable的,对大批量统计提供较好的支持。
    • 编码时注意的地方
      • 静态变量
      • 单例,是否延迟初始化
      • Spring bean里的属性(默认单例)
      • 访问共用缓存中的集合类List,Map,Set
      • 文档化线程安全问题
      • 集群产生并发
    • 数据库对并发的控制
      • 没有使用锁产生的问题:
      一个帐户上有100元
      操作员A查询当前余额得到100
      操作员B同时查询余额得到100
      A购买50元商品,更新余额为100-50=50
      B购买50元商品,更新余额为100-50=50
    • 悲观锁串行化处理
      • oracle行级锁
      • 锁住操作帐户
      • select * from account where id = 10 for update
      • 操作员A的事务先做,余额=50
      • 操作员B的事务再做,此时查询余额已经变为50
    • For update
      • SELECT cols FROM tables [WHERE...] FOR UPDATE [OF cols] [NOWAIT];
      • select a.c1,a.c2 from t1 a,t2 b where b.c3=1 and a.c1=b.c1 for update of a.c2 加of只会锁定of后的表
      • No wait 立即报错ORA-00054资源正忙
      • Wait n 等待n秒,报错ORA-30006
    • 乐观锁CAS
      • 增加一列版本号,每次更新数据时版本号加一,只允许高于当前版本的数据更新
      • 操作员A查询余额得到100,版本号1
      • 操作员B同时查询余额得到100,版本号1
      • A购买50元,余额为100-50=50,同时更新版本号(set version=2 where version=1 and…),更新返回成功记录数1,提交成功
      • B购买50元,提交版本号2,此时where version=1已经更新不到数据了,因此回滚或者进行其他操作
      • 适用并发较少的场景下
    • 帐务会计里锁的使用
      • 使用排他锁在数据库层面锁定数据
      • 锁的是帐号
      • 锁了Account表记录,操作SystemMoney表就不用再加锁了
      • 注意帐号排序,避免死锁
      • 使用缓冲记账方式避免锁竞争
    • That‘s all !Thank you!