为了账号安全,请及时绑定邮箱和手机立即绑定

两道分析题的处理

标签:
Java

今天下午一位前辈给我出了两道要写代码的分析题,说的晚上要交过去,现在已经很晚了。

题目

  1. 10个线程计算1000000个数的和

  2. 自己手写Spring事务的处理逻辑,包含传播级别的处理

题目1

在打完电话之后就直接写了,比我想象中要简单一些.

思路:

  1. 10个线程,那么每个线程负责数据中的一部分。

  2. 所有的数据完成之后要能通知最后的求和线程,所有线程都完成的话需要一个临界条件

  3. 每个线程处理逻辑基本一样

求和运算操作继承自Runnable接口,在操作完之后将求和线程的个数加1,并且判断是否全部都运算完毕,如果全部都算完了,那么通知主线程计算。

假设运算不会溢出

public class NumberSum {
    /**
     * 要计算的数的数量,10000
     */
    private static int CONST = 1000000;    /**
    * 总共的线程数量
    */
    private static int THREAD_COUTN = 10;    
    /**
    * 每个线程负责计算的数据块大小
    */
    private static int SEGMENT_COUNT = CONST / THREAD_COUTN;    
    /**
    * 要求和的所有数据存放位置
    */
    private static Long[] nums = new Long[CONST];    
    /**
    * 所有线程求出的总和存放位置
    */
    private static Long[] result = new Long[THREAD_COUTN];    
    /**
    * 完成求和的线程的个数
    */
    private static AtomicInteger complete = new AtomicInteger(0);    private static final Object lock = new Object();    public static void main(String[] args) {        long startTime = System.nanoTime();        for (int i = 0; i < CONST; i++) {
            nums[i] = Long.valueOf(i);
        }

        Compute compute = new Compute();        for (int i = 0; i < THREAD_COUTN; i++) {            new Thread(compute, String.valueOf(i)).start();
        }

        Long sum = 0L;
        synchronized (lock) {            try {
                System.out.println("等待计算结果");                // 可能其他线程已经计算完结果了,就无需等待 
                if (complete.get() < 10){
                    lock.wait();
                }
                System.out.println("计算完成");                for (int i = 0; i < THREAD_COUTN; i++) {
                    sum = sum + result[i];
                }

                System.out.println("最终求得的结果为:" + sum);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

        }        long endTime = System.nanoTime();        double second = (endTime - startTime) / Math.pow(10,9);
        System.out.println("总共运行时间:"  + second  + "秒");

    }    /**
     * Runable可以为多个线程共享
     */
    static class Compute implements Runnable {

        @Override        public void run() {            int segment = Integer.parseInt(Thread.currentThread().getName());            int beginIndex = segment * SEGMENT_COUNT;            int endIndex = (segment + 1) * SEGMENT_COUNT;
            Long sum = 0L;            for (int i = beginIndex; i < endIndex; i++) {
                sum = sum + nums[i];
            }
            result[segment] = sum;
            System.out.println("线程" + segment + "求得的结果为:" + sum);
            synchronized (lock){                int completeNumber = complete.addAndGet(1);                if (completeNumber == THREAD_COUTN){
                    lock.notifyAll();
                }
            }

        }
    }
}

题目2

Spring事务处理代码阅读

这个题目,我想了下,事务中包含了不少要了解的东西,我在写事务的时候,走偏了一点,低估了手写事务的难度,对事务的理解程度不够深刻,没有能很好的剥离出事务处理的核心部分。

几个核心类:

  • TransactionInterceptor AOP的Advice,方法拦截器,配置的时候用的是它

  • TransactionAspectSupport,事务拦截器的父类,提供了一些模板方法,早先的Spring版本,方法在TransactionInterceptor内,新版的Spring把一些方法放到这个对象中了。

  • PlatformTransactionManager ,真正处理事务生命周期的东西,begin,commit,rollback都在PlatformTransactionManager内实现

  • TransactionAttributeSource,这个对指定具体事务执行的属性进行了封装。具体事务配置的属性都在里面

Spring事务处理基本结构

1000

事务基本结构

else部分的逻辑暂时用不到

  1. 获取定义的attrbute,事务的配置部分,配置中是可以指定具体的事务管理器的。

  2. 根据事务的属性,判断是不是要创建事务,事务的传播级别处理都在里面,对应第277行

  3. 执行事务体内的方法。invocation.proceedWithInvocation()。我们的事务代码部分,一般就是我们队数据库的操作代码

  4. 如果有异常,处理异常的部分, 286行

  5. 不管有无异常执行完之后,清除事务信息。

  6. 如果正确执行的话,就commit结果,并且返回我们程序返回的值

Spring判断是否要创建事务

1000

截图自TransactionAspectSupport.java

  1. 重点部分在461行,事务属性与事务管理器都不为空的话,那么事务管理器根据指定的事务属性获取事务。

  2. 最后的prepareTransaction知识把事务信息绑定到当前线程。

事务管理器内部都是有关事务的操作,这个知识抽象的事务管理器,作为模板方法,真正的事务处理需要其继承类来实现。


640

image.png

事务传播级别的处理

抽象事务管理器内部,getTransaction主要用来处理 事务的传播级别的逻辑,代理一些方法到doGetTransaction,isExistingTransaction,doBegin方法上

public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException {
        Object transaction = doGetTransaction();        //缓存日志级别,避免重复检查
        boolean debugEnabled = logger.isDebugEnabled();        if (definition == null) {            // Use defaults if no transaction definition given.
            definition = new DefaultTransactionDefinition();
        }        if (isExistingTransaction(transaction)) {            // Existing transaction found -> check propagation behavior to find out how to behave.
            return handleExistingTransaction(definition, transaction, debugEnabled);
        }        // Check definition settings for new transaction.
        if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) {            throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout());
        }        // No existing transaction found -> check propagation behavior to find out how to proceed.
        if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) {            throw new IllegalTransactionStateException(                    "No existing transaction found for transaction marked with propagation 'mandatory'");
        }        else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW ||
                definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) {
            SuspendedResourcesHolder suspendedResources = suspend(null);            if (debugEnabled) {
                logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition);
            }            try {                boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER);
                DefaultTransactionStatus status = newTransactionStatus(
                        definition, transaction, true, newSynchronization, debugEnabled, suspendedResources);
                doBegin(transaction, definition);
                prepareSynchronization(status, definition);                return status;
            }            catch (RuntimeException ex) {
                resume(null, suspendedResources);                throw ex;
            }            catch (Error err) {
                resume(null, suspendedResources);                throw err;
            }
        }        else {            // Create "empty" transaction: no actual transaction, but potentially synchronization.
            if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) {
                logger.warn("Custom isolation level specified but no actual transaction initiated; " +                        "isolation level will effectively be ignored: " + definition);
            }            boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS);            return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null);
        }
    }

1000

传播级别处理的部分

  1. 如果存在了事务,那么处理已经存在的事务,在已经存在的事务内进行事务传播级别的处理。是否存在事务的代码交给其继承类来实现,自己默认返回false

  2. 没有事务的话,就直接根据事务传播级别的定义进行处理

1000

常见传播级别


使用NESTED传播级别的时候,底层数据源必须基于JDBC3.0,并且实现者需要支持保存点事务机制。


1000

NESTED事务传播级别

仿写的部分

代码内容:
基于AOP注解的方法拦截器,在进行方法拦截的时候,我们可以获取到方法上的注解值,通过注解的值,可以了解到事务的传播级别。

一个事务如何判断是否在其它事务内呢?
我用的是一个事务栈,使用LinkedList作为栈来处理,LinkedList是线程私有的,这样实现不同的线程也不会相互干扰,如果栈内已经存在事务标志了,那么就代表当前操作是一件在事务内的。

代码相对粗糙。

依赖:

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>4.3.16.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>4.3.16.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>4.3.16.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
    </dependencies>

事务拦截器:

/**
 * @author aihe 2018/7/16
 */public class TransactionInterceptor implements MethodInterceptor {    /**
     * 事务栈,为每个线程设置独立的事务栈,如果栈内存在标志,代表当前操作依据在事务内
     */
    private ThreadLocal<LinkedList<TransactionFlag>> tx = new ThreadLocal<LinkedList<TransactionFlag>>();    /**
     * 事务方法上指定的回滚事务异常类型。
     */
    private ThreadLocal<LinkedList<Class<? extends Throwable>[]>> excetionStack = new ThreadLocal<LinkedList<Class<? extends Throwable>[]>>();    /**
     * 1. 创建事务,根据当前的情况,判断是否要创建
     * 2. 执行事务内的实际的具体逻辑
     * 3. 如果发生异常,判断是否要回滚
     * @param methodInvocation
     * @return
     * @throws Throwable
     */
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {

        createTransactionIfNecessary(methodInvocation);
        Object result = null;        try {
            result = methodInvocation.proceed();
        } catch (Exception e) {
            completeTransactionAfterThrowing(e);            throw e;
        }        return result;
    }    /**
     * 处理事务回滚逻辑,如果不存在事务直接跳过,否则根据异常类型判断是否要进行事务回滚。
     * @param e
     * @throws Exception
     */
    private void completeTransactionAfterThrowing(Exception e) throws Exception {        if (tx.get() != null){
            Class<? extends Throwable>[] first = excetionStack.get().getFirst();            for (Class<? extends Throwable> aClass : first) {                if (e.getClass() == aClass) {
                    System.out.println("回滚事务");                    throw e;
                }
            }
        }
    }    private void createTransactionIfNecessary(MethodInvocation methodInvocation) {
        LinkedList<TransactionFlag> transactionFlags = tx.get();
        LinkedList<Class<? extends Throwable>[]> throwables = excetionStack.get();
        Method method = methodInvocation.getMethod();
        Transactional annotation = method.getAnnotation(Transactional.class);        //获取传播属性与异常
        Propagation propagation = annotation.propagation();
        Class<? extends Throwable>[] rollbackFor = annotation.rollbackFor();
        throwables.add(rollbackFor);        if (propagation == Propagation.MANDATORY) {            throw new IllegalTransactionStateException(                    "没有已经存在的事务");
        }        if (propagation == Propagation.REQUIRES_NEW ||
                propagation == Propagation.REQUIRED ||
                propagation == Propagation.NESTED
                ) {            //不存在事务
            if (transactionFlags == null ){
                transactionFlags = new LinkedList<TransactionFlag>();
                transactionFlags.add(new TransactionFlag());
                tx.set(transactionFlags);
                System.out.println("新建事务");            //已经存在事务
            }else{                if (propagation == Propagation.REQUIRES_NEW ){
                    transactionFlags.add(new TransactionFlag());
                    tx.set(transactionFlags);
                    System.out.println("挂起当前事务");
                }
            }
        }


    }    /**
     * 事务标志
     */
    private static class TransactionFlag {

    }
}

写不出那么厉害的代码... 有些像伪代码

最后

事务处理的代码这块自己写的确实不够好。

昨天挂完电话,前辈的意思是挂完电话,立刻写代码然后发给他吗,如果是这样,上面的算法计数题可以写的出来,
我事务的部分写的也是不会表现太好。

在思考事务如何写的时候,事务如果往大了写,那就是自己造一个精简版的事务轮子,我前两个小时是这么想的,但分析不到位,没有那么简单,也写不出来。
往小了写,事务的核心逻辑剥离出来,整体思路要明白。对于我,事务的原理要仔细研究啊。事务的代码表现的不够好。

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消