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

简介: 提交和回滚事件 Java的try-catch-finally语法结构不但使我们可以安全地处理异常,还能够在程序抛出异常时选择性地执行一些代码。同样地,我们 也可以控制程序在事务成功提交之后去执行某段代码,而当事务回滚时则去执行另一段代码。

提交和回滚事件

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


Java中的提交和回滚事件

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

public  class  Counter  {
	private  final  Ref<Integer>  value  =  new  Ref<Integer>(1);
	public  void  decrement()  {
		new  Atomic<Integer>()  {
			public  Integer  atomically()  {
				deferred(new  Runnable()  {
					public  void  run()  {
						System.out.println(
							"Transaction  completed...send  email,  log,  etc.");
					}
				});
			compensating(new  Runnable()  {
				public  void  run()  {
					System.out.println("Transaction  aborted...hold  the  phone");
				}
			});
			if(value.get()  <=  0)
				throw  new  RuntimeException("Operation  not  allowed");
			value.swap(value.get()  -  1);
			return  value.get();
			}
		}.execute();
	}
}

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

package  com.agiledeveloper.pcj;
public  class  UseCounter  {
	public  static  void  main(final  String[]  args)  {
		Counter  counter  =  new  Counter();
		counter.decrement();
		System.out.println("Let's  try  again...");
		try  {
			counter.decrement();
		}  catch(Exception  ex)  {
			System.out.println(ex.getMessage());
		}
	}
}

通过运行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。

class  Counter  {
	private  val  value  =  Ref(1)
	def  decrement()  =  {
		atomic  {
			deferred  {  println("Transaction  completed...send  email,  log,  etc.")  }
			compensating  {  println("Transaction  aborted...hold  the  phone")  }
			if(value.get()  <=  0)
				throw  new  RuntimeException("Operation  not  allowed")
			value.swap(value.get()  -  1)
			value.get()
		}
	}
}

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

package  com.agiledeveloper.pcj
object  UseCounter  {
	def  main(args  :  Array[String])  :  Unit  =  {
		val  counter  =  new  Counter()
		counter.decrement()
		println("Let's  try  again...")
		try  {
			counter.decrement()
		}  catch  {
			case  ex  =>  println(ex.getMessage())
		}
	}
}

如下所示,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


文章转自 并发编程网-ifeve.com

相关文章
|
JavaScript 前端开发
事件委托是JS技巧,通过绑定事件到父元素利用事件冒泡,减少事件处理器数量,提高性能和节省内存。
【6月更文挑战第27天】事件委托是JS技巧,通过绑定事件到父元素利用事件冒泡,减少事件处理器数量,提高性能和节省内存。例如,动态列表可共享一个`click`事件处理器,通过`event.target`识别触发事件的子元素,简化管理和响应动态内容变化。
273 0
|
C语言 Android开发 C++
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
本文介绍了使用MTuner软件进行Qt MinGW编译程序的内存泄漏检测的方法,提供了MTuner的下载链接和测试代码示例,并通过将Debug程序拖入MTuner来定位内存泄漏问题。
701 4
基于MTuner软件进行qt的mingw编译程序的内存泄漏检测
|
安全 测试技术 数据库
代码危机:“内存溢出” 事件的深度剖析与反思
初涉编程时,我坚信严谨逻辑能让代码顺畅运行。然而,“内存溢出”这一恶魔却以残酷的方式给我上了一课。在开发电商平台订单系统时,随着订单量增加,系统逐渐出现处理迟缓甚至卡死的情况,最终排查发现是订单状态更新逻辑中的细微错误导致内存无法及时释放,进而引发内存溢出。这次经历让我深刻认识到微小错误可能带来巨大灾难,从此对待代码更加谨慎,并养成了定期审查和测试的习惯。
365 0
|
设计模式 uml
在电脑主机(MainFrame)中只需要按下主机的开机按钮(on()),即可调用其它硬件设备和软件的启动方法,如内存(Memory)的自检(check())、CPU的运行(run())、硬盘(Hard
该博客文章通过一个电脑主机启动的示例代码,展示了外观模式(Facade Pattern)的设计模式,其中主机(MainFrame)类通过调用内部硬件组件(如内存、CPU、硬盘)和操作系统的启动方法来实现开机流程,同时讨论了外观模式的优缺点。
|
设计模式 存储 安全
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:设计一个线程安全的单例类并解释其内存占用情况?使用Java多线程工具类实现一个高效的线程池,并解释其背后的原理。结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
574 1
|
监控 Rust 安全
Rust代码在公司电脑监控软件中的内存安全监控
使用 Rust 语言开发的内存安全监控软件在企业中日益重要,尤其对于高安全稳定性的系统。文中展示了如何用 Rust 监控内存使用:通过获取向量长度和内存大小来防止泄漏和溢出。此外,代码示例还演示了利用 reqwest 库自动将监控数据提交至公司网站进行实时分析,以保证系统的稳定和安全。
1319 2
|
设计模式 存储 缓存
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
Java面试题:结合建造者模式与内存优化,设计一个可扩展的高性能对象创建框架?利用多线程工具类与并发框架,实现一个高并发的分布式任务调度系统?设计一个高性能的实时事件通知系统
422 0
|
设计模式 存储 缓存
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
Java面试题:结合单例模式与Java内存模型,设计一个线程安全的单例类?使用内存屏障与Java并发工具类,实现一个高效的并发缓存系统?结合观察者模式与Java并发框架,设计一个可扩展的事件处理系统
273 0
|
Linux 调度
部署02-我们一般接触的是Mos和Wimdows这两款操作系统,很少接触到Linux,操作系统的概述,硬件是由计算机系统中由电子和机械,光电元件所组成的,CPU,内存,硬盘,软件是用户与计算机接口之间
部署02-我们一般接触的是Mos和Wimdows这两款操作系统,很少接触到Linux,操作系统的概述,硬件是由计算机系统中由电子和机械,光电元件所组成的,CPU,内存,硬盘,软件是用户与计算机接口之间
|
存储
阿里云轻量应用服务器收费标准价格表:200Mbps带宽、CPU内存及存储配置详解
阿里云香港轻量应用服务器,200Mbps带宽,免备案,支持多IP及国际线路,月租25元起,年付享8.5折优惠,适用于网站、应用等多种场景。
3822 0