今天下午一位前辈给我出了两道要写代码的分析题,说的晚上要交过去,现在已经很晚了。
题目
- 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判断是否要创建事务
- 重点部分在461行,事务属性与事务管理器都不为空的话,那么事务管理器根据指定的事务属性获取事务。
- 最后的prepareTransaction知识把事务信息绑定到当前线程。
事务管理器内部都是有关事务的操作,这个知识抽象的事务管理器,作为模板方法,真正的事务处理需要其继承类来实现。
事务传播级别的处理
抽象事务管理器内部,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,并且实现者需要支持保存点事务机制。
仿写的部分
代码内容:
基于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 {
}
}
写不出那么厉害的代码... 有些像伪代码
最后
事务处理的代码这块自己写的确实不够好。
昨天挂完电话,前辈的意思是挂完电话,立刻写代码然后发给他吗,如果是这样,上面的算法计数题可以写的出来,
我事务的部分写的也是不会表现太好。
在思考事务如何写的时候,事务如果往大了写,那就是自己造一个精简版的事务轮子,我前两个小时是这么想的,但分析不到位,没有那么简单,也写不出来。
往小了写,事务的核心逻辑剥离出来,整体思路要明白。对于我,事务的原理要仔细研究啊。事务的代码表现的不够好。