Titan源码阅读——如何透明地支持多线程事务隔离

本文主要是从代码层面解读上一篇博文(Titan线程隔离的验证)中Titan的实现机制,即如何用一个TitanGraph对象来透明地支持多线程间的事务隔离。(注:源码使用titan-0.5.4版本)

TitanGraph是一个接口,当我们使用TitanFactory.open(…)获得一个TitanGraph时,返回的都是一个StandardTitanGraph对象。 但StandardTitanGraph类并没有实现TitanGraph的所有接口,StandardTitanGraph继承自抽象类TitanBlueprintsGraph。实际上所有对TitanGraph的基本操作(如 addVertex、addEdge、commit、rollback等),也即Blueprints定义的接口,都是在这个类中实现的。
具体的类继承图就是这样的:
StandardTitanGraph的继承图

再细看TitanBlueprintsGraph对Blueprints接口的实现,发现都是先调用了getAutoStartTx方法,然后再进行操作:

public abstract class TitanBlueprintsGraph implements TitanGraph {

    ……

    public TitanVertex addVertex(Object id) {
        return getAutoStartTx().addVertex(id);
    }

    @Override
    public TitanVertex addVertex() {
        return getAutoStartTx().addVertex();
    }

    @Override
    public TitanVertex getVertex(long id) {
        return getAutoStartTx().getVertex(id);
    }

    ……

    @Override
    public EdgeLabel getOrCreateEdgeLabel(String name) {
        return getAutoStartTx().getOrCreateEdgeLabel(name);
    }

    @Override
    public EdgeLabel getEdgeLabel(String name) {
        return getAutoStartTx().getEdgeLabel(name);
    }
}

getAutoStartTx方法返回的是一个TitanBlueprintsTransaction对象,因此对图的操作就是在一个Transaction里进行操作。
再看getAutoStartTx到底是如何返回一个transaction的:

    private TitanBlueprintsTransaction getAutoStartTx() {
        if (txs == null) ExceptionFactory.graphShutdown();
        TitanBlueprintsTransaction tx = txs.get();  // 获取当前线程对应的事务
        if (tx == null) {    // 如果当前线程没有对应的事务
            tx = (TitanBlueprintsTransaction) newThreadBoundTransaction(); // 新建一个事务
            txs.set(tx);     // 保存到txs中
            openTx.put(tx, Boolean.TRUE);
            log.debug("Created new thread-bound transaction {}", tx);
        }
        return tx;
    }

变量 txs 很关键,是一个全局变量:

private ThreadLocal<TitanBlueprintsTransaction> txs = new ThreadLocal<TitanBlueprintsTransaction>() {
   protected TitanBlueprintsTransaction initialValue() {
       return null;
   }
};

ThreadLocal是java自带的类,一个变量被定义为ThreadLocal<T>的,则每个线程都拥有一个独立的 T 类型的变量,通过get/set函数可以读写这个值。这就相当于定义了一个全局的Map<Thread, T>,每个线程可以get/set到自己的变量,大家拥有的变量值是相互独立的。更具体的关于ThreadLocal的介绍可以看这篇文章:[Java并发包学习七]解密ThreadLocal
既然使用了ThreadLocal,代码就非常简单了,不需要任何synchronized的同步语句,因为各线程操作的都是自己的那份变量,各个线程都有自己的一个transaction对象。因此,当不同线程对同一个TitanGraph对象进行操作的时候,实际上是化归为对它们自己的事务对象的操作,这也就做到了线程间的事务隔离了。

最后再看一下 TitanGraph 的 commit 和 rollback 函数,具体的实现在 TitanBlueprintsGraph 中:

    @Override
    public void commit() {
        TitanTransaction tx = txs.get();  // 获取当前线程对应的事务
        if (tx != null && tx.isOpen()) {
            try {
                tx.commit();   // 提交事务
            } finally {
                txs.remove();  // 将事务从txs中删除
                openTx.remove(tx);
                log.debug("Committed thread-bound transaction {}", tx);
            }
        }
    }

    @Override
    public void rollback() {
        TitanTransaction tx = txs.get();  // 获取当前线程对应的事务
        if (tx != null && tx.isOpen()) {
            try {
                tx.rollback();  // 回滚事务
            } finally {
                txs.remove();   // 将事务从txs中删除
                openTx.remove(tx);
                log.debug("Rolled back thread-bound transaction {}", tx);
            }
        }
    }

这两个函数都是一样,对具体的 transaction 对象执行操作之后,将它从 ThreadLocal 变量 txs 中删除。这样当该线程下次再操作 TitanGraph 时,就会(在 getAutoStartTx 函数中)生成一个新的 transaction 记录到 txs 中,即对应地又打开一个事务。
最后再看一下 TitanGraph 的 shutdown 函数:

    @Override
    public synchronized void shutdown() {
        for (TitanTransaction tx : openTx.keySet()) {
            tx.commit();
        }
        openTx.clear();
        txs = null;
    }

openTx 是一个 Map<TitanBlueprintsTransaction, Boolean> 类型,记录哪些 transaction 是打开的。该函数对所有打开的 transaction 执行 commit 操作。并最后把 txs 置空,这样这个 TitanGraph 对象就不能再被操作了(见getAutoStartTx函数的第一行代码)。

综上所述,我们从源码中可以看出 TitanGraph 对象不仅仅是线程安全的,而且透明地为每个线程打开一个事务,使得每个线程里的操作都是事务隔离的。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值