1

コードを最適化しようとしていて、時間のかかる操作を行うスレッドを生成したいと考えています。その最適化の実装中に、私は頭がおかしくなるような問題に遭遇しました。問題を単純化し、その特定の問題のテスト ケースを作成しました: (SpringJUnit4ClassRunner を使用しているため、testCRUD メソッドの開始時にトランザクションが適切に開始されます)

スレッドで foundParent が null である理由を誰かが理解するのを手伝ってくれますか?

private Semaphore sema = new Semaphore(0, false);
private long parentId;

@Test
public void testCRUD() {
    //create
    DBParent parent = null;
    {
        parent = new DBParent();
        parentDao.persist(parent);
        parentId = parent.getId();
        assertTrue(parentId > 0);

        parentDao.flush();
    }

    (new Thread(
        new Runnable() {
            public void run() 
            {
                System.out.println("Start adding childs !");
                DBParent foundParent = parentDao.findById(parentId);
                assertTrue(foundParent != null); //ASSERTION FAILS HERE !!!!

                System.out.println("Releasing semaphore !");
                sema.release();
                System.out.println("End adding childs !");
            }
    })).start();

    try {
        System.out.println("Acquiring semaphore !");
        sema.acquire();
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

=============================編集=================== =============== コメントの 1 つの提案に従って、スレッドを生成する threadManager Bean を作成しました。threadManager のコードは次のとおりです。

public class ThreadManager {
    @Transactional(propagation=Propagation.REQUIRES_NEW)
    public void executeTask(String Name, Runnable task) {
        (new Thread(task, Name)).start();
    }
 }

次に、前のテストでは、スレッドを手動で見つめる代わりに、次のようにスレッド マネージャーに投稿しました。

@Autowired private ParentDao parentDao;
@Autowired private ThreadManager threadManager;

private Semaphore sema = new Semaphore(0, false);
private long parentId;

@Test
public void testCRUD() {
    //create
    DBParent parent = null;
    {
        parent = new DBParent();
        parentDao.persist(parent);
        parentId = parent.getId();
        assertTrue(parentId > 0);

        parentDao.flush();
    }

    threadManager.executeTask("BG processing...",
        new Runnable() {
            public void run() 
            {
                System.out.println("Start adding childs !");
                DBParent foundParent = parentDao.findById(parentId);
                assertTrue(foundParent != null); //ASSERTION FAILS HERE !!!!

                System.out.println("Releasing semaphore !");
                sema.release();
                System.out.println("End adding childs !");
            }
    });

    try {
        System.out.println("Acquiring semaphore !");
        sema.acquire();
    }
    catch (InterruptedException e) {
        e.printStackTrace();
    }
}

残念ながら、これも機能しません!!! :-(

4

2 に答える 2

0

Spring トランザクションを新しいスレッドにバインドして、トランザクションと Hibernate/JPA アクセスを実行できますただし、これは他のスレッドとは異なる TX および JPA/HB セッションでなければなりません。

の Spring コードはOpenSessionInViewFilter、Hibernate セッションを Spring の TX 管理にバインドする方法の適切な例です。これを削除して、かなり最小限のコードにすることができます。

見る:

  • org.springframework.orm.hibernate3.support.OpenSessionInViewFilter
  • OpenSessionInViewFilter.doFilterInternal() -- これが実際にバインドする場所です
  • TransactionSynchronizationManager.bindResource()
  • TransactionSynchronizationManager.unbindResource()
  • TransactionSynchronizationManager.getResource()

あるプロジェクト (IIRC) では、この機能を「ServerThreadHb」クラスにラップして、構築時に以前のスレッドバインディングをセットアップして保存restore()し、ブロックで呼び出されるメソッドを使用して、finally以前のバインディングを復元しました。

投稿されたコード サンプルでは、​​作業が完了するのを同期的に待機するため、別のスレッドで作業を実行しても意味がありません。ただし、その制約を削除してその機能を拡張することを計画していたと思います。

于 2013-11-03T00:02:28.733 に答える