今天下午一位前輩給我出了兩道要寫代碼的分析題,說的晚上要交過去,現在已經很晚了。
題目
- 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 {
}
}
寫不出那麼厲害的代碼... 有些像僞代碼
最後
事務處理的代碼這塊自己寫的确實不夠好。
昨天挂完電話,前輩的意思是挂完電話,立刻寫代碼然後發給他嗎,如果是這樣,上面的算法計數題可以寫的出來,
我事務的部分寫的也是不會表現太好。
在思考事務如何寫的時候,事務如果往大了寫,那就是自己造一個精簡版的事務輪子,我前兩個小時是這麼想的,但分析不到位,沒有那麼簡單,也寫不出來。
往小了寫,事務的核心邏輯剝離出來,整體思路要明白。對于我,事務的原理要仔細研究啊。事務的代碼表現的不夠好。