今天下午一位前辈给我出了两道要写代码的分析题,说的晚上要交过去,现在已经很晚了。
题目
10个线程计算1000000个数的和
自己手写Spring事务的处理逻辑,包含传播级别的处理
解
题目1
在打完电话之后就直接写了,比我想象中要简单一些.
思路:
10个线程,那么每个线程负责数据中的一部分。
所有的数据完成之后要能通知最后的求和线程,所有线程都完成的话需要一个临界条件
每个线程处理逻辑基本一样
求和运算操作继承自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事务处理基本结构
事务基本结构
else部分的逻辑暂时用不到
获取定义的attrbute,事务的配置部分,配置中是可以指定具体的事务管理器的。
根据事务的属性,判断是不是要创建事务,事务的传播级别处理都在里面,对应第277行
执行事务体内的方法。invocation.proceedWithInvocation()。我们的事务代码部分,一般就是我们队数据库的操作代码
如果有异常,处理异常的部分, 286行
不管有无异常执行完之后,清除事务信息。
如果正确执行的话,就commit结果,并且返回我们程序返回的值
Spring判断是否要创建事务
截图自TransactionAspectSupport.java
重点部分在461行,事务属性与事务管理器都不为空的话,那么事务管理器根据指定的事务属性获取事务。
最后的prepareTransaction知识把事务信息绑定到当前线程。
事务管理器内部都是有关事务的操作,这个知识抽象的事务管理器,作为模板方法,真正的事务处理需要其继承类来实现。
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); } }
传播级别处理的部分
如果存在了事务,那么处理已经存在的事务,在已经存在的事务内进行事务传播级别的处理。是否存在事务的代码交给其继承类来实现,自己默认返回false
没有事务的话,就直接根据事务传播级别的定义进行处理
常见传播级别
使用NESTED传播级别的时候,底层数据源必须基于JDBC3.0,并且实现者需要支持保存点事务机制。
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 { } }
写不出那么厉害的代码... 有些像伪代码
最后
事务处理的代码这块自己写的确实不够好。
昨天挂完电话,前辈的意思是挂完电话,立刻写代码然后发给他吗,如果是这样,上面的算法计数题可以写的出来,
我事务的部分写的也是不会表现太好。
在思考事务如何写的时候,事务如果往大了写,那就是自己造一个精简版的事务轮子,我前两个小时是这么想的,但分析不到位,没有那么简单,也写不出来。
往小了写,事务的核心逻辑剥离出来,整体思路要明白。对于我,事务的原理要仔细研究啊。事务的代码表现的不够好。