软件事务内存导论(八)提交和回滚事件

简介:

提交和回滚事件

Java的try-catch-finally语法结构不但使我们可以安全地处理异常,还能够在程序抛出异常时选择性地执行一些代码。同样地,我们也可以控制程序在事务成功提交之后去执行某段代码,而当事务回滚时则去执行另一段代码。StmUtils中的deferred()和compensatiing()这两个函数分别提供了上述功能。特别地,在实现事务的过程中,为保证事务能顺利完成,我们通常会加入一些带副作用的逻辑,而deferred()函数则是一个执行所有这部分逻辑的绝佳地点。


Java中的提交和回滚事件

我们可以把想要在事务成功完成之后执行的代码放在Runnable接口实现部分的代码块中,并将其作为参数传给StmUtils的deferred()函数。同样地,我们也可以把想要在事务失败之后执行的代码封装在Runnable接口中传给compensating()函数。由于这两个函数必须在事务的环境下运行,所以我们只有在automically()函数的函数体中才能调用他们。

01 public  class  Counter  {
02     private  final  Ref<Integer>  value  =  new  Ref<Integer>(1);
03     public  void  decrement()  {
04         new  Atomic<Integer>()  {
05             public  Integer  atomically()  {
06                 deferred(new  Runnable()  {
07                     public  void  run()  {
08                         System.out.println(
09                             "Transaction  completed...send  email,  log,  etc.");
10                     }
11                 });
12             compensating(new  Runnable()  {
13                 public  void  run()  {
14                     System.out.println("Transaction  aborted...hold  the  phone");
15                 }
16             });
17             if(value.get()  <=  0)
18                 throw  new  RuntimeException("Operation  not  allowed");
19             value.swap(value.get()  -  1);
20             return  value.get();
21             }
22         }.execute();
23     }
24 }

在Counter类的定义代码中我们看到,Counter类仅含有一个名为decrement()的实例方法。在这个方法中,我们继承了Atomic类并实现了atomically()函数。在前面的例子中,我们都仅仅是简单地把事务的逻辑代码放在这个位置。而现在,除了原有的逻辑代码之外,我们把事务成功和事务回滚之后要执行的代码也放到了atomically()里面。下面让我们构建一个简单的测试用例来验证一下Counter的功能:


01 package  com.agiledeveloper.pcj;
02 public  class  UseCounter  {
03     public  static  void  main(final  String[]  args)  {
04         Counter  counter  =  new  Counter();
05         counter.decrement();
06         System.out.println("Let's  try  again...");
07         try  {
08             counter.decrement();
09         catch(Exception  ex)  {
10             System.out.println(ex.getMessage());
11         }
12     }
13 }

通过运行UseCounter,我们可以清楚地观察到事务成功完成和失败时程序的执行逻辑:


Transaction  aborted...hold  the  phone
Transaction  completed...send  email,  log,  etc.
Let's  try  again...
Transaction  aborted...hold  the  phone
Operation  not  allowed


当第一次调用decrement()函数并成功完成事务之后,封装在deferred()函数内的代码逻辑将被执行。而当我们第二次调用decrement()时,由于事务执行过程中抛出了异常,所以事务将被回滚,而封装在compensating()函数内的代码也将被执行。最后我们需要注意的是,输出结果中最顶部的那个非预期的重试是由我们之前在6.9节中曾讨论过的默认优化设置所导致的。

deferred()函数是一个执行事务收尾工作以便使其效果固化的绝佳地点,所以我们可以在里面随便进行打印、显示消息、发布通知以及提交数据库事务等操作。如果我们在事务之外有什么遗留的工作待完成,那么这个函数无疑是最好的完成地点。与deferred()类似的是,compensating()函数是记录事务失败信息的好地方。此外,如果我们之前已经将非托管对象(即那些没有使用Akka Ref进行管理的对象)与托管对象混杂在一起的话,那么这里也是纠正这一错误的合适地点——但是由于这种做法太容易出错,所以请你最好避免采用这样的设计思路。

Scala中的提交和回滚事件

在Scala中,我们处理提交和回滚事件的方式与Java基本相同,唯一区别就是在Scala中我们可以将闭包/函数值直接传递给deferred()和compensating()。下面让我们将Counter类由Java转译成Scala。


01 class  Counter  {
02     private  val  value  =  Ref(1)
03     def  decrement()  =  {
04         atomic  {
05             deferred  {  println("Transaction  completed...send  email,  log,  etc.")  }
06             compensating  {  println("Transaction  aborted...hold  the  phone")  }
07             if(value.get()  <=  0)
08                 throw  new  RuntimeException("Operation  not  allowed")
09             value.swap(value.get()  -  1)
10             value.get()
11         }
12     }
13 }

在上面的代码中,我们将事务运行成功时所要执行的那部分代码封装在一个闭包中,然后将其作为参数传递给deferred()函数。类似地,事务回滚时所要执行的代码也被作为一个闭包赋给了compensating()函数。与此同时,这两个函数又与事务逻辑代码一起被置于表示atomic()函数的闭包当中。这段代码再次彰显了Scala在语法上简洁明了的特征。下面让我们将UseConuter类也从Java转译成Scala:


01 package  com.agiledeveloper.pcj
02 object  UseCounter  {
03     def  main(args  :  Array[String])  :  Unit  =  {
04         val  counter  =  new  Counter()
05         counter.decrement()
06         println("Let's  try  again...")
07         try  {
08             counter.decrement()
09         catch  {
10             case  ex  =>  println(ex.getMessage())
11         }
12     }
13 }

如下所示,Scala版代码的执行结果与Java版的结果是完全相同的:


Transaction  aborted...hold  the  phone
Transaction  completed...send  email,  log,  etc.
Let's  try  again...
Transaction  aborted...hold  the  phone
Operation  not  allowed


目录
相关文章
|
6月前
|
Rust 监控 并行计算
用Rust构建电脑网络监控软件:内存安全性和多线程编程
在当今数字化世界中,网络安全一直是至关重要的问题。电脑网络监控软件是确保网络系统安全和高效运行的关键工具。然而,编写电脑网络监控软件需要处理复杂的多线程编程和内存安全性问题。Rust编程语言提供了一种强大的方式来构建安全的电脑网络监控软件,同时避免了许多常见的编程错误。
275 0
|
5月前
|
存储 数据库 C语言
Hawkeyes: x86软件迁移Arm的弱内存序问题解决方案
本文介绍了x86软件迁移到Arm过程中可能遇到的弱内存序问题的解决方案,解析了弱内存序问题的根因,介绍了Hawkeyes的架构和实现原理。欢迎有需求的团队发送邮件咨询
600 0
|
11月前
|
存储 Java Apache
Apache ZooKeeper - ZK的内存数据 + 持久化事务日志 + 数据快照 初探
Apache ZooKeeper - ZK的内存数据 + 持久化事务日志 + 数据快照 初探
142 0
|
11月前
|
存储 缓存 程序员
MIPS架构深入理解10-向MIPS移植软件之内存序
MIPS架构深入理解10-向MIPS移植软件之内存序
|
缓存 安全
CleanMyMac X稳定版苹果电脑磁盘内存清理软件
Mac电脑用的时间久了,Mac用户尤其是MacBook用户会经常收到“磁盘几乎已满”的提示,如何解决这个问题,当我们使用苹果MAC一段时间后,就会有大量的垃圾文件占用磁盘空间,例如系统缓存文件、应用程序缓存文件、备份和重复文件、旧版的应用程序及其部件等,为了不影响电脑的后续使用,我们需要经常清理磁盘空间,这里分享一个处理磁盘空间不足提示的小方法。CleanMyMac X下载:http://t.csdn.cn/ye9sT
120 0
|
缓存 运维 负载均衡
Redis连环炮:内存淘汰?事务?分布式锁?分步式限流?异步队列?延时队列?高可用?如何部署?哈希槽?数据库和缓存的数据一致性?
Redis连环炮:内存淘汰?事务?分布式锁?分步式限流?异步队列?延时队列?高可用?如何部署?哈希槽?数据库和缓存的数据一致性?
184 0
Redis连环炮:内存淘汰?事务?分布式锁?分步式限流?异步队列?延时队列?高可用?如何部署?哈希槽?数据库和缓存的数据一致性?
|
算法 Java API
【Android 逆向】Android 逆向基本概念 ( 软件运行时内存结构 | 文件与内存之间的联系 )
【Android 逆向】Android 逆向基本概念 ( 软件运行时内存结构 | 文件与内存之间的联系 )
207 0
【Android 逆向】Android 逆向基本概念 ( 软件运行时内存结构 | 文件与内存之间的联系 )