あなたの@Asyncをプログラミング春の非同期は、実際にそれを非同期?最もファッショナブルな非同期の七フィートのピットを〜

少し長いはじめに

赤ちゃんのフロントエンドは、Ajax、ないの喜びと非同期プログラミングを使用します-私たちは、非同期のjavaを持っているだけでなく、彼らがより幸せにそれらを使用する-私たちBIA〜JI〜A 注(gǒupí)解(gāoyào)、私たちは栗の以下の風を見てみましょう...幸せな男です。

登録ユーザーは、初期化のポイント自分のアカウントに(も登録インセンティブとして想像することができます)、メッセージを送信し、登録通知メッセージを送信し、ユーザーを与えるために、( 只是举栗子,切莫牛角大法)、このようなプロセスは、例えば、我々は追加のトランザクションを一损俱损的那种、)SMSメールと私たちは、主要プロセスを下支えする必要を分割しないことができると思います。

今日、このビジネスは、私は自分自身に考えて、これは~~~~~ハッハッハまあと係合するためにノートパッドではありません

ピット循環依存関係

コードを見て:

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserService userService;
    
     @Override
    @Transactional(rollbackFor = Exception.class, propagation = Propagation.REQUIRES_NEW)
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User 保存用户成功:" + user);
        userService.senMsg(user);
        userService.senEmail(user);
        return insert;
    }


    @Async
    @Override
    public Boolean senMsg(User user) {
        try {
            TimeUnit.SECONDS.sleep(2);
            System.out.println("发送短信中:.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",手机号:" + user.getMobile() + "发送短信成功");
        return true;
    }

    @Async
    @Override
    public Boolean senEmail(User user) {
        try {
            TimeUnit.SECONDS.sleep(3);
            System.out.println("发送邮件中:.....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",邮箱:" + user.getEmail() + "发送邮件成功");
        return true;
    }
复制代码

結果:、春の循環依存関係の問題を起動しません。おそらく、ちょうどジュニアパートナーの一部はショックを受けるだろう、と述べました。春は、それはそれの循環依存関係によってサポートされている依存のサイクルを、解決の問題ではないのですか?どのようにでしょうか?

確かに、前に私はそう確信していたが、各時間も試験しました。あなたは現在、強いと私は同じ考えを持っている場合、異常せUnsatisfiedDependencyExceptionhas been injected into other beans [userServiceImpl] in its raw version as part of a circular reference,抱擁あなたは、そう直面していない裸である、ことをお勧めしますcircular reference

循環依存春の豆をいえば、といくつかの小さなパートナーは不慣れであってもよいし、すべての後に、開発プロセスは自由な流通知覚の概念に依存しているようです。実際には、あなたがの春で働く権利ので、このような錯覚を持っている幼児期ので、あなたは「聞かせて、心の平和:実際には、我々のコードは、私たちはこのように、依存性のサイクルを書くことを考えることができます- 」

@Service
public class AServiceImpl implements AService {
    @Autowired
    private BService bService;
    ...
}
@Service
public class BServiceImpl implements BService {
    @Autowired
    private AService aService;
    ...
}
复制代码

実験でまとめ @Asyncを使用して、循環依存関係の問題につながる必要な条件を表示されます。

  1. 私たちは、支持体上になっ@EnableAsync
  2. サイクルが依存するビーン@Asyncノート

ピット非同期失敗

まあ、依存のサイクル以来、私たちは円形ではなく、依存関係は、私たちは来ることができません。

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    SendService sendService;
    
    @Override
    @Transactional()
    public int save(UserDTO userDTO) {
        User user = new User();
        BeanCopyUtils.copy(userDTO, user);
        int insert = userMapper.insert(user);
        System.out.println("User 保存用户成功:" + user);
        this.senMsg(user);
        this.senEmail(user);
        return insert;
    }


    @Async
    @Override
    public Boolean senMsg(User user) {
        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",手机号:" + user.getMobile() + "发送短信成功");
        return true;
    }

    @Async
    @Override
    public Boolean senEmail(User user) {
        System.out.println(Thread.currentThread().getName() + "给用户id:" + user.getId() + ",邮箱:" + user.getEmail() + "发送邮件成功");
        return true;
    }
复制代码

我々はいくつかをテストした結果が、私は結果を印刷しました:

2019-08-05 21:59:32.304  INFO 14360 --- [nio-8080-exec-3] com.alibaba.druid.pool.DruidDataSource   : {dataSource-1} inited
2019-08-05 21:59:32.346 DEBUG 14360 --- [nio-8080-exec-3] c.b.lea.mybot.mapper.UserMapper.insert   : ==>  Preparing: insert into t_user (username, sex, mobile,email) values (?, ?, ?,?) 
2019-08-05 21:59:32.454 DEBUG 14360 --- [nio-8080-exec-3] c.b.lea.mybot.mapper.UserMapper.insert   : ==> Parameters: 王麻子(String), 男(String), 18820158833(String), [email protected](String)
2019-08-05 21:59:32.463 DEBUG 14360 --- [nio-8080-exec-3] c.b.lea.mybot.mapper.UserMapper.insert   : <==    Updates: 1
User 保存用户成功:User(id=101, username=王麻子, mobile=18820158833, [email protected], sex=男, password=123435, createTime=Mon Aug 05 12:20:51 CST 2019, updateTime=null)
发送短信中:.....
http-nio-8080-exec-3给用户id:101,手机号:18820158833发送短信成功
发送邮件中:.....
http-nio-8080-exec-3给用户id:101,邮箱:[email protected]发送邮件成功
复制代码

これは、スレッドがまだ不快ではない私の愛を感じていない白いブラインド、白書き込みです~~? http-nio-8080-exec-3なぜ次の結論で始まり、ああ、話をするん?:

実験でまとめ、失敗に非同期@Asyncリードを使用する理由が発生します。

  1. 非同期を使用すると、このクラスでは、非同期サポートしていません。
  2. 呼び出し側は、実際にはこれです、それは現在のオブジェクトではなく、実際のプロキシオブジェクトでuserService、春には、この方法によって傍受することはできませんオンラインソリューションを持って、呼び出すために、このクラスではそうではないではないと呼ばれているapplicationContext.getBean(AInterface.class)AopContext.currentProxy()、私は現在、テストはまだエラー段階で、成功していません私は勉強するときに成功ここに追加先占位...

ピットの3つの基本タイプ

私は、上に行くために、成功の兆候を郵送、ああ参照するには、テキストメッセージを書きたかったです

第四に、ピットトランザクション障害

何(...異常)このショートメッセージインタフェースは、(私たちのほとんどは、3ウェイ・インターフェースを使用している)、少し不快を郵送の場合

事故

あなたは、非同期の原理を理解する必要があります

@Async非同期

  • 実際にこの方法スキャンをスキャンするとき、含まれている場合、春は動的に生成されたBeanのサブクラスになり、我々は(jdkProxy)プロキシクラスを呼び出すBean内のノート@Asyncスプリングが含まれている、プロキシクラスは、私たちに継承されています豆によって書かれ、その後で来たプロキシクラスに注入し、この時点では、このアプローチの実装ではプロキシクラスになることを、プロキシクラスを決定するために、このメソッドは非同期に実行する必要がある場合、彼らは(私たちは、もともと書いた親クラスを呼び出すことはありません、豆)は、対応する方法。
  • 春には、このスレッドプールのキューを読み取るために待機して、キューに置かれ、キュー自身、彼が実行する必要があるような方法を維持し、実装プロセスを完了し、これ非同期関数を完了します。
  • パラメータは、私たちは、スレッド・プールの数を設定することができますがあるとき私たちは、再設定の作業に集中することができます。この実装のので、@Asyncコメントを追加すると呼ばれる同じクラスのメソッドは無効です!あなたが同じクラスにいるとき、メソッド呼び出しがクラス本体で実行されているので、春には(...なぜ、これはおよそ以下の話をされたメソッドの呼び出しをインターセプトすることはできません奸笑...嘻嘻嘻嘻...)。
  • さらにその1つのステップでは、春はAOP、アスペクト指向の機能を提供してくれます。彼の原理および非同期注釈の原理は、容器が定義されたクラス区分として走査されるばね開始類似しています。これらのクラスが注入されている場合は、あなたがこれらのメソッドを呼び出すときに、注入されたエージェントクラスは、プロキシクラスは、自然と呼ばれています。親クラスの実装方法は、AOPの実装を完了するためにコードの一部を実行する直前春対応するプロキシクラスを介して、コールの後どこへ行きますか!

SpringBoot環境は、@Asyncノートを使用するために、我々は、起動クラスの@EnableAsyncコメントを追加する必要があります。起動クラスの@EnableSchedulingを追加する必要がSpringBootの注釈とともに@Scheduled使用することは、同じ理由(もちろん、古いXMLの構成が可能であるが、SpringBoot環境では、我々は全体のノートの開発をお勧めします使用)、特定の原則であります私たちは、下記を分析します。私たちは、新しいスレッドが非同期操作を開始したときにメソッドを呼び出すことで有効にする場合は@EnableAsyncコメントを追加した後、我々は唯一このアプローチに@Asyncコメントを追加する必要があり、もちろん、前提は、あるクラスが春になっている必要があり、この方法はその環境。

示例:非spingboot项目
<task:annotation-driven executor="annotationExecutor" />
<!-- 支持 @Async 注解 -->
<task:executor id="annotationExecutor" pool-size="20"/>
复制代码

実装プロセス:

  1. スキャンは、ノートオンになっているEnableAsyncEnableAsync @にコメントがあり、@Import(AsyncConfigurationSelector.class)springbootが古いルーチンに注入し、
  2. 会場を再してくださいAsyncConfigurationSelectorを参照してくださいselectImports、まだ方法をここで使用してデフォルトを使用しているProxyAsyncConfigurationこの構成のクラスを
  3. 観察し続けてProxyAsyncConfiguration連続してAbstractAsyncConfiguration、それはだの内側に、setConfigurers我々が達成できることを示しAsyncConfigurer、それ以外の場合は例外がスローされます、スレッドプールだけでなく、例外ハンドラを設定するインターフェイスを完了することが、春の環境でのみ実装クラスを持つことができます。コードのビットには:
    /**
	 * Collect any {@link AsyncConfigurer} beans through autowiring.
	 */
	@Autowired(required = false)
	void setConfigurers(Collection<AsyncConfigurer> configurers) {
      
		if (CollectionUtils.isEmpty(configurers)) {
			return;
		}
      //AsyncConfigurer用来配置线程池配置以及异常处理器,而且在Spring环境中最多只能有一个,在这里我们知道了,如果想要自己去配置线程池,只需要实现AsyncConfigurer接口,并且不可以在Spring环境中有多个实现AsyncConfigurer的类。
		if (configurers.size() > 1) {
			throw new IllegalStateException("Only one AsyncConfigurer may exist");
		}
		AsyncConfigurer configurer = configurers.iterator().next();
		this.executor = configurer.getAsyncExecutor();
		this.exceptionHandler = configurer.getAsyncUncaughtExceptionHandler();
	}
复制代码
  1. ProxyAsyncConfiguration豆注射AsyncAnnotationBeanPostProcessor、これはBeanPostBeanPostProcessor明らかに(例えば、非同期動作をトリガすることができ注釈されるであろう@Asyncビーン処理)
  2. 私たちは、ことに注意してAsyncAnnotationBeanPostProcessor親クラスを書き換えることがありsetBeanFactory、この方法は、それはそれに慣れて少しですBeanFactoryAwareインタフェースメソッド、AsyncAnnotationBeanPostProcessor初期化Beanのは、言及される前に、親クラスがこのインタフェースを実装して、我々は長い時間を分析しましたこのインターフェースは、インターフェースの豆認識タイプを実装して、豆の初期化中に適切な初期化メソッドを呼び出します、あなたは特定見ることができますAbstractAutowireCapableBeanFactory#initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd)方法を
  3. 豆プロセスのpostProcessAfterInitialization上位クラスのメソッドAbstractAdvisingBeanPostProcessorの。私たちは、ソースコードから見ることができます。AsyncAnnotationBeanPostProcessor豆は、後工程でありますBeanPostProcessor
  4. 最後剤JdkDynamicAopProxyinvokeメソッドは、責任のパターンのチェーンの使用である:List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);チェーンに知らせる、実行するプロキシインターセプト通知を生成するsetBeanFactory()メソッドが含まれ、実行連鎖の作成に使用されるReflectiveMethodInvocationオブジェクトを、そして最終的に呼び出しが完了します強調処理方法、分岐が最後に実行されます方法ReflectiveMethodInvocationproceed()proceed()
  5. 実際の実装は、AsyncExecutionInterceptorinvoke()
  6. :が上記の春環境は一つだけ持つことができますAsyncConfigurer実装クラスを、それは春の環境での春の環境で唯一のスレッドプールの構成は、複数のスレッド・プールを構成することができ、そして我々が使用できることを意味するものではありません@Asyncときに、キャリアとしてタスク・スレッド・プールを指定することができるように、スレッドプールのBean名のプロパティの値によって指定された非同期操作のための注釈は、以下を参照してください。determineAsyncExecutor

ロングったらしい兄弟:

我々は使用を容易にしたい場合は@Async注釈は、このように、デフォルトのスレッド・プールの構成で構成されただけAsyncConfigurerインタフェースを(実現するために、中SpringBootで非同期操作を可能にします、それはデフォルトであるため、当然のことながら、これだけ、春の環境でクラスを必要とします引数の優先順位を決定する一つの、これ以上の実装クラス)を持つことができ、スレッドプールの設定に加え、起動クラスに@EnableAsyncコメントのために、あなたは@Async注釈を有効にすることができます。

私たちも、明示的に@Async注釈、Bean名のごキュータタイプとして指定された注釈値を使用する場合、あなたが指定したスレッドプールを使用することができ、私たちは春の環境でキュータビーンの複数のタイプを設定することができ、AsyncConfigurerを実装することはできませんキャリアタスクとして、あなたがスレッドプールを使用するように、また、より柔軟です。

小さなデザート

だから、:たとえば、一般的な:私たちは多くの方法がある、非同期プログラミングをSpringの非同期プログラミングの親愛を使用しますFuture的syncCompletableFuture.supplyAsync,@Async、ハハ本当に切っても切れないThread.start()...であるので、私は冗談を言いました:

アリスとボブ:お父さんは2人の子供を持っていました。お父さんは、飲む彼は赤ワイン、赤外を購入できるようにしたかったです。その後、父が突然、喫煙したいので、タバコを買うためにお父さん暁明をしましょう。そのようなこの操作赤などのものを購入しなければならないタバコを買いに行くための構造やマルチスレッド同期、そして暁明によると、思考の被写体の顔には、一般的に、そして、物事を購入する方法として、それを買いましたそれが終了します。これは、時間のコストが増加します(尿中の父親の場合にはそれを保持?)。非同期は、この問題を解決することです。あなたは別に、あなたは自分自身のあなた自身のことを行うことができ、彼らは物事を購入できるように、赤暁明に指示を出したことができますので、それの結果を受信したときに彼らが買いました。

package com.boot.lea.mybot.futrue;

/**
 * @ClassName: TestFuture
 * @Description: 演示异步编程
 * @author LiJing
 * @date 2019/8/5 15:16
 */
@SuppressWarnings("all")
public class TestFuture {
    static ExecutorService executor = Executors.newFixedThreadPool(2);

    public static void main(String[] args) throws InterruptedException {
        //两个线程的线程池
        //小红买酒任务,这里的future2代表的是小红未来发生的操作,返回小红买东西这个操作的结果
        CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> {
            System.out.println("爸:小红你去买瓶酒!");
            try {
                System.out.println("小红出去买酒了,女孩子跑的比较慢,估计5s后才会回来...");
                Thread.sleep(5000);
                return "我买回来了!";
            } catch (InterruptedException e) {
                System.err.println("小红路上遭遇了不测");
                return "来世再见!";
            }
        }, executor);

        //小明买烟任务,这里的future1代表的是小明未来买东西会发生的事,返回值是小明买东西的结果
        CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
            System.out.println("爸:小明你去买包烟!");
            try {
                System.out.println("小明出去买烟了,可能要3s后回来...");
                Thread.sleep(3000);

                throw new InterruptedException();
//                return "我买回来了!";
            } catch (InterruptedException e) {
                System.out.println("小明路上遭遇了不测!");
                return "这是我托人带来的口信,我已经不在了。";
            }
        }, executor);

        //获取小红买酒结果,从小红的操作中获取结果,把结果打印
        future2.thenAccept((e) -> {
            System.out.println("小红说:" + e);
        });
        //获取小明买烟的结果
        future1.thenAccept((e) -> {
            System.out.println("小明说:" + e);
        });

        System.out.println("爸:等啊等 西湖美景三月天嘞......");
        System.out.println("爸: 我觉得无聊甚至去了趟厕所。");
        Thread.currentThread().join(9 * 1000);
        System.out.println("爸:终于给老子买来了......huo 酒");
        //关闭线程池
        executor.shutdown();
    }
}
复制代码

結果:

おすすめ

転載: juejin.im/post/5d47a80a6fb9a06ad3470f9a