软件事务内存导论(六)配置Akka事务

配置Akka事务

默认情况下,Akka为其相关的运行参数都设定了默认值,我们可以通过代码或配置文件akka.conf来更改这些默认设置。如果想了解如何指定或修改该配置文件位置的详细信息,请参阅Akka的文档。

针对单个事务,我们可以利用TransactionFactory在程序代码中更改其设置。下面就让我们用这种方式先后在Java和Scala中更改一些设置来为你展示如何实现设置的变更。

在Java中对事务进行配置

01 public  class  CoffeePot  {
02     private  static  final  Ref<Integer>  cups  =  new  Ref<Integer>(24);
03     public  static  int  readWriteCups(final  boolean  write)  {
04         final  TransactionFactory  factory  =
05         new  TransactionFactoryBuilder().setReadonly(true).build();
06         return  new  Atomic<Integer>(factory)  {
07             public  Integer  atomically()  {
08                 if(write)  cups.swap(20);
09                 return  cups.get();
10             }
11         }.execute();
12     }

为了能够用编程的方式对事务进行配置,我们需要一个TransactionFactory实例对象,而TransactionFactoryBuilder则为我们提供了很多方便的函数用于创建该Factory。在上例中,我们创建了一个TranactionFactoryBuilder实例对象,并调用该对象的setReadonly()函数来为TransactionFactory添加readonly选项。由于TransactionFactoryBuilder实现了Cascade[1]设计模式,所以我们可以将更多用于改变事务属性的函数串在一起挂在TransactionFactoryBuilder构造函数之后、build()函数之前。随后我们把factory的实例对象作为Atomic的一个构造函数参数传给它,这样就保证了该事务内的所有操作都不会变更任何托管引用。

通过上述设置我们已经将readWriteCups()变成了一个只读事务,接下来你肯定希望了解在一个只读事务中试图改变引用的值将会产生什么后果。下面我们会调用两次readWriteCups(),第一次仅仅是读取cups引用的内容,而第二次调用则会尝试改变cups引用的值。

01     public  static  void  main(final  String[]  args)  {
02         System.out.println("Read  only");
03         readWriteCups(false);
04         System.out.println("Attempt  to  write");
05         try  {
06             readWriteCups(true);
07         catch(Exception  ex)  {
08             System.out.println("Failed  "  +  ex);
09         }
10     }
11 }

由于被设置成了只读,所以readWriteCups()事务不欢迎变更请求。于是当我们试图更改cups引用的值时,系统抛出了org.multiverse.api.exceptions.ReadonlyException异常,并且整个事务也将回滚。

Read  only
Attempt  to  write
Failed  org.multiverse.api.exceptions.ReadonlyException:
Can't  open  for  write  transactional  object  'akka.stm.Ref@1272670619'
because  transaction  'DefaultTransaction'  is  readonly'

上述运行时异常是在调用引用的swap()的时候抛出来的。该函数的作用是当且仅当新值与当前值不同时,将其引用改为指向新值的地址;否则,该函数将忽略变更请求。所以在本例中,如果我们在调用swap()时将参数20换成与当前cpus引用的值相等的24,则系统就不会抛出任何异常。

在Scala中对事物进行配置

在Scala中,我们可以使用atomic()函数代替Atomic类来创建事务,该函数在使用时需要一个TransactionFactory类型的可选参数。同时,由于我们能够在伙伴对象(companion object)上使用工厂方法,所以创建factory实例也比在Java中要简单许多。

01 object  CoffeePot  {
02     val  cups  =  Ref(24)
03     def  readWriteCups(write  :  Boolean)  =  {
04         val  factory  =  TransactionFactory(readonly  =  true)
05         atomic(factory)  {
06             if(write)  cups.swap(20)
07             cups.get()
08         }
09     }
10     def  main(args  :  Array[String])  :  Unit  =  {
11         println("Read  only")
12         readWriteCups(false)
13         println("Attempt  to  write")
14         try  {
15             readWriteCups(true)
16         catch  {
17             case  ex  =>  println("Failed  "  +  ex)
18         }
19     }
20 }

除了在代码方面保持了Scala和Akka特有的简洁优雅之外,上述代码与Java版本就没有什么其他不同之处了,所以代码的执行结果也毫无意外地和Java版本完全相同。

Read  only
Attempt  to  write
Failed  org.multiverse.api.exceptions.ReadonlyException:
Can't  open  for  write  transactional  object  'akka.stm.Ref@1761506447'
because  transaction  'DefaultTransaction'  is  readonly'


[1]近些年来,特别是随着JVM上新语言的不断涌现,由Kent Beck所著的《Smalltalk Best Practice Patterns》[Bec96]一书中所讨论的一些设计模式又被重新发掘了出来。

时间: 2024-07-31 17:43:10

软件事务内存导论(六)配置Akka事务的相关文章

软件事务内存导论(三)用Akka/Multiverse STM实现并发

用Akka/Multiverse STM实现并发 上面我们已经学习了如何在Clojure里使用STM,我猜你现在一定很好奇如何在Java代码中使用STM.而对于这一需求,我们有如下选择: 直接在Java中使用Clojure STM.方法非常简单,我们只需将事务的代码封装在一个Callable接口的实现中就行了,详情请参见第7章. 喜欢用注解(annotation)的开发者可能会更倾向于使用Multiverse的STM API. 除了STM之外,如果我们计划使用角色(actor),那么还可以考虑选

软件事务内存导论(九) 集合与事务

集合与事务 在我们努力学习这些示例的过程中,很容易就会忘记我们所要处理的值都必须是不可变的.只有实体才是可变的,而状态值则是不可变的.虽然STM已经为我们减轻了很多负担,但如果想要在维护不可变性的同时还要兼顾性能的话,对我们来说也将是一个非常严峻的挑战. 为了保证不可变性,我们采取的第一个步骤是将单纯用来保存数据的类(value classes)及其内部所有成员字段都置为final(在Scala中是val).然后,我们需要传递地保证我们自己定义的类里面的字段所使用的类也都 是不可变的.可以说,将

软件事务内存导论

前言 软件事务内存 用Akka/Multiverse STM实现并发 创建事务 创建嵌套事务 配置Akka事务 阻塞事务 提交和回滚事件 集合与事务 处理写偏斜异常 STM的局限性 文章转自 并发编程网-ifeve.com

spring mybatis配置了事务,存储过程也配置了事务

问题描述 spring mybatis配置了事务,存储过程也配置了事务 spring mybatis配置了事务,存储过程也配置了事务,如果存储过程事务回滚了,spring配置的事务会回滚吗? 解决方案 mybatis spring事务配置MyBatis Spring 注解事务配置spring整合mybatis之事务配置 解决方案二: 那得看具体的情况了,存储过程回滚的原因是? 解决方案三: 存储过程事务一般在数据库中自己设置,spring的事务控制service层等,两都一般分开,不会相互影响

软件事务内存导论(五)创建嵌套事务

1.1    创建嵌套事务 在之前的示例中,每个用到事务的方法都是各自在其内部单独创建事务,并且事务所涉及的变动也都是各自独立提交的.但如果我们想要将多个方法里的事务调整成一个统一的原子操作的时候,上述做法就无能为力了,所以我们需要使用嵌套事务来实现这一目标. 通过使用嵌套事务,所有被主控函数调用的那些函数所创建的事务都会默认被整合到主控函数的事务中.除此之外,Akka/Multiverse还提供 了很多其他配置选项,如新隔离事务(new isolated transactions)等.总之,使

软件事务内存导论(二)软件事务内存

1.1    软件事务内存 将实体与状态分离的做法有助于STM(软件事务内存)解决与同步相关的两大主要问题:跨越内存栅栏和避免竞争条件.让我们先来看一下在Clojure上下文中的STM是什么样子,然后再在Java里面使用它. 通过将对内存的访问封装在事务(transactions)中,Clojure消除了内存同步过程中我们易犯的那些错误(见 <Programming Clojure>[Hal09]和<The Joy of Clojure>[FH11]).Clojure会敏锐地观察和

软件事务内存导论(四)创建事务

创建事务 我们创建事务的目的是为了协调针对多个托管引用的变更.事务将会保证这些变更是原子的,也就是说,所有的托管引用要么全部被提交要么全部被丢弃,所以在事务之外我们将不会看到有任何局部变更(partial changes)出现.此外,我们也可以用创建事务的方式来解决对单个ref先读后写所引发的相关问题. Akka是用Scala开发出来的,所以如果我们工作中用的是Scala的话,就可以直接幸福地享用Akka简洁明了的API了.对于那些日常工作中不能使用Scala开发的程序员,Akka同样也提供了一

软件事务内存导论(七)阻塞事务

阻塞事务--有意识地等待 我们经常会遇到这样一种情况,即某事务T能否成功完成依赖于某个变量是否发生了变化,并且由于这种原因所引起的事务运行失败也可能只是暂时性的.作为对这种暂时性失败的响应,我们可能会返回一个错误码并告诉事务T等待一段时间之后再重试.然而在事务T等待期间,即使其他任务已经更改了事务T所依赖的数据,事务T也没法立即感知到并重试了.为了解决这一问题,Akka为我们提供了一个简单的工具--retry(),该函数可以先将事务进行回滚,并将事务置为阻塞状态直到该事物所依赖的引用对象发生变化

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

处理写偏斜异常 在6.6节中,我们曾经简单讨论了写偏斜(write skew)以及Clojure STM是如何解决这个问题的.Akka同样提供了处理写偏斜问题的支持,但是需要我们配置一下才能生效.OK,一听到配置这个词可能让你觉得有些提心吊 胆,但实际操作起来其实起来还是蛮简单的.下面就让我们首先了解一下Akka在不进行任何配置情况下的默认行为. 让我们回顾一下之前曾经见到过的那个多个账户共享同一个联合余额最低限制例子.首先我们创建了一个名为Portfolio的类来保存支票账户余额和 储蓄账户余