软件事务内存导论(十)处理写偏斜异常

简介:

处理写偏斜异常

在6.6节中,我们曾经简单讨论了写偏斜(write skew)以及Clojure STM是如何解决这个问题的。Akka同样提供了处理写偏斜问题的支持,但是需要我们配置一下才能生效。OK,一听到配置这个词可能让你觉得有些提心吊胆,但实际操作起来其实起来还是蛮简单的。下面就让我们首先了解一下Akka在不进行任何配置情况下的默认行为。

让我们回顾一下之前曾经见到过的那个多个账户共享同一个联合余额最低限制例子。首先我们创建了一个名为Portfolio的类来保存支票账户余额和储蓄账户余额。根据银行规定,这两个账户的总余额不得低于$1000。在Portfolio类的代码中我们用Java重新实现了withdraw()函数。在该函数中,我们先读取两个账户的余额,将二者相加得到总余额,并在等待一个故意插进去的延时(引入这个延时的目的是为了人为制造事务冲突的环境)之后,从其中一个账户余额中减掉给定数量的金额(当然,在操作之前需要判断减掉这个数量后总余额不少于$1000)。最后需要注意的是,withdraw()函数是在一个使用了默认设置的事务中完成上述操作的。


01 public  class  Portfolio  {
02     final  private  Ref<Integer>  checkingBalance  =  new  Ref<Integer>(500);
03     final  private  Ref<Integer>  savingsBalance  =  new  Ref<Integer>(600);
04     public  int  getCheckingBalance()  {  return  checkingBalance.get();  }
05     public  int  getSavingsBalance()  {  return  savingsBalance.get();  }
06  
07     public  void  withdraw(final  boolean  fromChecking,  final  int  amount)  {
08         new  Atomic<Object>()  {
09             public  Object  atomically()  {
10                 final  int  totalBalance  =
11                     checkingBalance.get()  +  savingsBalance.get();
12                 try  {  Thread.sleep(1000);  }  catch(InterruptedException  ex)  {}
13                 if(totalBalance  -  amount  >=  1000)  {
14                     if(fromChecking)
15                         checkingBalance.swap(checkingBalance.get()  -  amount);
16                     else
17                         savingsBalance.swap(savingsBalance.get()  -  amount);
18                 }
19                 else
20                     System.out.println(
21                         "Sorry,  can't  withdraw  due  to  constraint  violation");
22                     return  null;
23                 }
24         }.execute();
25     }
26 }

下面让我们创建两个事务来并发地更改账户内的余额:


01 public  class  UsePortfolio  {
02     public  static  void  main(final  String[]  args)  throws  InterruptedException  {
03         final  Portfolio  portfolio  =  new  Portfolio();
04         int  checkingBalance  =  portfolio.getCheckingBalance();
05         int  savingBalance  =  portfolio.getSavingsBalance();
06         System.out.println("Checking  balance  is  "  +  checkingBalance);
07         System.out.println("Savings  balance  is  "  +  savingBalance);
08         System.out.println("Total  balance  is  "  +
09             (checkingBalance  +  savingBalance));
10         final  ExecutorService  service  =  Executors.newFixedThreadPool(10);
11         service.execute(new  Runnable()  {
12             public  void  run()  {  portfolio.withdraw(true100);  }
13         });
14         service.execute(new  Runnable()  {
15             public  void  run()  {  portfolio.withdraw(false100);  }
16         });
17         service.shutdown();
18         Thread.sleep(4000);
19         checkingBalance  =  portfolio.getCheckingBalance();
20         savingBalance  =  portfolio.getSavingsBalance();
21         System.out.println("Checking  balance  is  "  +  checkingBalance);
22         System.out.println("Savings  balance  is  "  +  savingBalance);
23         System.out.println("Total  balance  is  "  +
24         (checkingBalance  +  savingBalance));
25         if(checkingBalance  +  savingBalance  <  1000)
26         System.out.println("Oops,  broke  the  constraint!");
27     }
28 }

正如我们在输出结果中所看到的那样,在默认情况下,Akka没能避免写偏斜问题,两个事务违反了银行的规定,即都从账户里取出了钱。


Checking  balance  is  500
Savings  balance  is  600
Total  balance  is  1100
Checking  balance  is  400
Savings  balance  is  500
Total  balance  is  900
Oops,  broke  the  constraint!


现在到了该彻底解决这个问题的时候了。让我们祭出TransactionFactory这个能帮助我们在程序里对事物进行配置的法宝,在Portfolio类的第9行插入下面这段创建工厂实例的代码:


1 akka.stm.TransactionFactory  factory  =
2     new  akka.stm.TransactionFactoryBuilder()
3         .setWriteSkew(false)
4         .setTrackReads(true)
5         .build();

在插进来的这几行代码中,我们创建了一个TransactionFactoryBuilder,并将writeSkew和trackReads属性分别设置为false和true。与Clojure STM对于ensure的处理类似,这两个设置项的目的是告诉事务要在其运行过程中对读操作进行追踪,同时也会使事务在读数据的过程中对账户余额变量加读锁直至提交开始为止。

除了上面提到的几处更改之外,Portfolio和UsePortfolio的其他代码都保持不变。而在对事务进行了上述设置之后,其输出结果如下所示:


Checking  balance  is  500
Savings  balance  is  600
Total  balance  is  1100
Sorry,  can't  withdraw  due  to  constraint  violation
Checking  balance  is  400
Savings  balance  is  600
Total  balance  is  1000


由于并发执行的不可预测性,我们不能确定两个事务到底哪个会胜出。但是我们可以从输出结果中看到,在所有操作结束后两个账户的余额是不同的,而在6.6节的Clojure示例中,最终两个账户余额是相同的。我们可以通过多次运行这两个实例来观察二者之间的差异。

在本节我们是用Java完成整个示例的。如果换成Scala,则我们可以使用在6.10节中学习的语法来配置事务的writeSkew和trackReads属性。 

目录
相关文章
|
4月前
|
存储 Linux 程序员
Linux内存管理宏观篇(二):不同角度去看内存(软件)
Linux内存管理宏观篇(二):不同角度去看内存(软件)
85 0
|
1月前
|
设计模式 uml
在电脑主机(MainFrame)中只需要按下主机的开机按钮(on()),即可调用其它硬件设备和软件的启动方法,如内存(Memory)的自检(check())、CPU的运行(run())、硬盘(Hard
该博客文章通过一个电脑主机启动的示例代码,展示了外观模式(Facade Pattern)的设计模式,其中主机(MainFrame)类通过调用内部硬件组件(如内存、CPU、硬盘)和操作系统的启动方法来实现开机流程,同时讨论了外观模式的优缺点。
|
2月前
|
Linux 调度
部署02-我们一般接触的是Mos和Wimdows这两款操作系统,很少接触到Linux,操作系统的概述,硬件是由计算机系统中由电子和机械,光电元件所组成的,CPU,内存,硬盘,软件是用户与计算机接口之间
部署02-我们一般接触的是Mos和Wimdows这两款操作系统,很少接触到Linux,操作系统的概述,硬件是由计算机系统中由电子和机械,光电元件所组成的,CPU,内存,硬盘,软件是用户与计算机接口之间
|
3月前
|
监控 Rust 安全
Rust代码在公司电脑监控软件中的内存安全监控
使用 Rust 语言开发的内存安全监控软件在企业中日益重要,尤其对于高安全稳定性的系统。文中展示了如何用 Rust 监控内存使用:通过获取向量长度和内存大小来防止泄漏和溢出。此外,代码示例还演示了利用 reqwest 库自动将监控数据提交至公司网站进行实时分析,以保证系统的稳定和安全。
176 2
|
11月前
|
Rust 监控 并行计算
用Rust构建电脑网络监控软件:内存安全性和多线程编程
在当今数字化世界中,网络安全一直是至关重要的问题。电脑网络监控软件是确保网络系统安全和高效运行的关键工具。然而,编写电脑网络监控软件需要处理复杂的多线程编程和内存安全性问题。Rust编程语言提供了一种强大的方式来构建安全的电脑网络监控软件,同时避免了许多常见的编程错误。
329 0
|
4月前
|
监控 算法 搜索推荐
C++内部监控软件:内存管理与性能调优的完美结合
在当今高度竞争的软件开发领域,内存管理和性能调优是构建高效应用的两个关键方面。本文将介绍一种基于C++的内部监控软件,通过结合精细的内存管理和有效的性能调优,实现了出色的应用性能。我们将深入探讨一些示例代码,演示如何在代码层面实现内存管理和性能优化,最后介绍如何将监控到的数据自动提交到网站。
286 1
|
4月前
|
存储 缓存 安全
从软件和硬件角度去看内存
从软件和硬件角度去看内存
78 0
|
10月前
|
存储 数据库 C语言
Hawkeyes: x86软件迁移Arm的弱内存序问题解决方案
本文介绍了x86软件迁移到Arm过程中可能遇到的弱内存序问题的解决方案,解析了弱内存序问题的根因,介绍了Hawkeyes的架构和实现原理。欢迎有需求的团队发送邮件咨询
1069 0
|
安全 测试技术
Win系统 - 内存稳定性测试软件(MemTest)
Win系统 - 内存稳定性测试软件(MemTest)
666 0
Win系统 - 内存稳定性测试软件(MemTest)
|
存储 Unix Shell
软件运行机制及内存管理
软件运行机制及内存管理
154 0

热门文章

最新文章

相关实验场景

更多