今天下午一位前辈给我出了两道要写代码的分析题,说的晚上要交过去,现在已经很晚了。
题目
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 {
}
}写不出那么厉害的代码... 有些像伪代码
最后
事务处理的代码这块自己写的确实不够好。
昨天挂完电话,前辈的意思是挂完电话,立刻写代码然后发给他吗,如果是这样,上面的算法计数题可以写的出来,
我事务的部分写的也是不会表现太好。
在思考事务如何写的时候,事务如果往大了写,那就是自己造一个精简版的事务轮子,我前两个小时是这么想的,但分析不到位,没有那么简单,也写不出来。
往小了写,事务的核心逻辑剥离出来,整体思路要明白。对于我,事务的原理要仔细研究啊。事务的代码表现的不够好。
随时随地看视频