《Java高并发程序设计》学习 --7.11 软件事务内存

45 篇文章 0 订阅
在一些函数式编程语言中,支持一种叫做软件事务内存(STM)的技术。这里的事务和数据库中所说的事务非常类似,具有隔离性、原子性和一致性。与数据库事务不同的是,内存事务不具备持久性。
在很多场合,某一项功能可能要由多个Actor协作完成。在这种协作事务中,如果一个Actor处理失败,那么根据事务的原子性,其他Actor所进行的操作必须要回滚。下面,看一个简单的案例。
假设有一个公司要给它的员工发福利,公司账户里有100元。每次,公司账户会给员工账户转一笔钱,假设转账10元,那么公司账户中应该减去10元,同时,员工账户中应该增加10元。这两个操作必须同时完成,或者同时不完成。
首先,看一下主函数中是如何启动一个内存事务的:
public class STMDemo {
	public static ActorRef company = null;
	public static ActorRef employee = null;

	public static void main(String[] args) throws Exception {
		final ActorSystem system = ActorSystem.create("transactionDemo", ConfigFactory.load("samplehello.conf"));
		company = system.actorOf(Props.create(CompanyActor.class), "company");
		employee = system.actorOf(Props.create(EmployeeActor.class), "employee");

		Timeout timeout = new Timeout(1, TimeUnit.SECONDS);

		for(int i=1; i<20; i++) {
			company.tell(new Coordinated(i,timeout), ActorRef.noSender());
			Thread.sleep(200);
			Integer companyCount = (Integer) Await.result(Patterns.ask(company, "GetCount", timeout), timeout.duration());
			Integer employeeCount = (Integer) Await.result(Patterns.ask(employee, "GetCount", timeout), timeout.duration());

			System.out.println("company count=" + companyCount);
			System.out.println("employee count=" + employeeCount);
			System.out.println("===============");
		} 
	}
}
上述代码中,CompanyActor和EmployeeActor分别用于管理公司账户和雇员账户。在12~23行中,尝试进行19次汇款,第一次汇款额度为1元,第二次为2元,依次类推,最后一笔汇款为19元。
在第13行,新建一个Coordinated协调者,并且将这个协调者当做消息发送给company,当company收到这个协调者信息后,自动成为这个事务的第一个成员。
第15~18行询问公司账户和雇员账户的当前余额,并在第20~21行进行输出。
下面是代表公司账户的Actor:
public class CompanyActor extends UntypedActor {
	private Ref.View<Integer> count = STM.newRef(100);
	
	@Override
	public void onReceive(Object msg) throws Exception {
		if(msg instanceof Coordinated) {
			final Coordinated c = (Coordinated)msg;
			final int downCount = (Integer)c.getMessage();
			STMDemo.employee.tell(c.coordinate(downCount), getSelf());
			try {
				c.atomic(new Runnable() {
					@Override
					public void run() {
						if(count.get() < downCount) {
							throw new RuntimeException("less than " + downCount);
						}
						STM.increment(count, -downCount);
					}
				});
			} catch (Exception e) {
				e.printStackTrace();
			}

		} else if("GetCount".equals(msg)) {
			getSender().tell(count.get(), getSelf());
		} else {
			unhandled(msg);
		}
	}
}
在CompanyActor中,首先判断接收的msg是否是Coordinated。如果是Coordinated,则表示这是一个新事务的开始。在第8行,获得事务的参数也就是需要转账的金额。接着在第9行,将调用Coordinated.coordinate()方法,将employee也加入当前事务中,这样这个事务中就有两个参与者。
第11行,调用了Coordinated.atomic()定义了原子执行块作为这个事务的一部分。在这个执行块中,对公司账户进行余额调整(第17行)。但是当汇款额度大于可用额度时,就会抛出异常,宣告失败。
第25行用于处理GetCount消息,返回当前账户余额。
作为转账接收的雇员账户如下:
public class EmployeeActor extends UntypedActor {
	private Ref.View<Integer> count = STM.newRef(50);

	@Override
	public void onReceive(Object msg) throws Exception {
		if(msg instanceof Coordinated) {
			final Coordinated c = (Coordinated)msg;
			final int downCount = (Integer)c.getMessage();
			try {
				c.atomic(new Runnable() {
					@Override
					public void run() {
						STM.increment(count, downCount);
					}
				});
			} catch (Exception e) {
				// TODO: handle exception
			}
		} else if("GetCount".equals(msg)) {
			getSender().tell(count.get(), getSelf());
		} else {
			unhandled(msg);
		}
	}
} 
上述代码第2行,设置雇员账户初始金额是50元。第6行,判断消息是否为Coordinated,如果是Coordinated,则当前Actor会自动加入Coordinated指定的事务。第10行,定义原子操作,在这个操作中将修改雇员账户余额。在这里,并没有给出异常情况的判断,只要接收到转入金额,一律将其增加到雇员账户中。
在这里,两个Actor都已经加入到同一个协调事务Coordinated中,因此当公司账户出现异常后,雇员账户就会回滚。
执行上述程序,部分输出如下:
company count=55
employee count=95
===============
company count=45
employee count=105
===============
company count=34
employee count=116
===============
company count=22
employee count=128
===============
company count=9
employee count=141
===============
java.lang.RuntimeException: less than 14
. . . .
company count=9
employee count=141
===============
可以看到,无论转账操作是否成功,公司账户和雇员账户的金额总是一致的。当转账失败时,雇员账户的余额并不会增加。这就是软件事务内存的作用。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值