2017-08-30 10 views
9

私はSQLiteデータベースにアクセスするアプリケーションを開発中です。問題は、DBにクエリがあるときにロックされることです。ほとんどの場合、アプリの流れは非常に線形であるため、これは問題ではありません。Javaでデータベースにアクセスするスレッドを管理する

しかし、私は非常に長い計算プロセスをユーザーがトリガーしています。このプロセスでは、計算の間にデータベースへの複数の呼び出しが行われます。

私はJavafxのprogressIndicatorとJavafx.Concurrencyフレームワークからのサービスを使用していたので、ユーザーに視覚的なフィードバックを求めていました。 これは、ユーザーがアプリケーションを自由に移動できるようにし、潜在的にデータベースへの他の呼び出しを引き起こすことになります。

これは、データベースファイルがロックされているという例外を引き起こしました。 このケースが発生したときにそのスレッドが実行されないようにする方法がありますが、私は明確な例をオンラインで見つけることができませんでした。それらのほとんどは単純化されており、スケーラビリティのある方法が欲しいです。私はキャンセル()メソッドを使用してみましたが、これはスレッドが時間内にキャンセルされることを保証するものではありません。

isCancelledのコードのすべての部分をチェックインできないため、スレッドがキャンセルされてから効果的に停止するまでに時間がかかることがあります。

私は以下の解決策を考えましたが、効率性と競争状態の回避と吊り下げの面でより良い方法があるかどうかを知りたいと思います。

// Start service 
    final CalculatorService calculatorService = new CalculatorService(); 

    // Register service with thread manager 
    threadManager.registerService(CalculatorService); 

    // Show the progress indicator only when the service is running 
    progressIndicator.visibleProperty().bind(calculatorService.runningProperty()); 

calculatorService.setOnSucceeded(new EventHandler<WorkerStateEvent>() { 
     @Override 
     public void handle(WorkerStateEvent workerStateEvent) { 
      System.out.println("SUCCEEDED"); 
      calculatorService.setStopped(true); 
     } 
    }); 

    // If something goes wrong display message 
    calculatorService.setOnFailed(new EventHandler<WorkerStateEvent>() { 
     @Override 
     public void handle(WorkerStateEvent workerStateEvent) { 
      System.out.println("FAILED"); 
      calculatorService.setStopped(true); 
     } 
    }); 

    // Restart the service 
    calculatorService.restart(); 

これは、サービスは、このインタフェースを実装

public class CalculatorService extends Service implements CustomService { 
    private AtomicBoolean stopped; 
    private CalculatorService serviceInstance; 

    public FindBundleService() { 
     stopped = new AtomicBoolean(false); 
     instance = this; 
    } 

    @Override 
    protected Task<Results> createTask() { 
     return new Task<Result>() { 

      @Override 
      protected Result call() throws Exception { 
       try { 
        Result = calculationMethod(this, serviceInstance); 
        return Result; 
       } catch (Exception ex) { 
        // If the thread is interrupted return 
        setStopped(true); 
        return null; 
       } 
      } 
     }; 
    } 

    @Override 
    public boolean isStopped() { 
     return stopped.get(); 
    } 

    @Override 
    public void setStopped(boolean stopped) { 
     this.stopped.set(stopped); 
    } 
} 

(停止または停止していない)私は、サービスの状態を設定するために使用できるメソッドが含まれるようにサブクラス化してきた私のサービスクラスであるI

public interface CustomService { 

    /** 
    * Method to check if a service has been stopped 
    * 
    * @return 
    */ 
    public boolean isStopped(); 

    /** 
    * Method to set a service as stopped 
    * 
    * @param stopped 
    */ 
    public void setStopped(boolean stopped); 

} 

すべてのサービスは、シングルトンクラスであるスレッドマネージャに登録する必要があります。アプリ内の任意の場所で

public class ThreadManager { 

    private ArrayList<CustomService> services; 

    /** 
    * Constructor 
    */ 
    public ThreadManager() { 
     services = new ArrayList<CustomService>(); 
    } 

    /** 
    * Method to cancel running services 
    */ 
    public boolean cancelServices() { 
     for(CustomService service : services) { 
      if(service.isRunning()) { 
       ((Service) service).cancel(); 
       while(!service.isStopped()) { 
        // Wait for it to stop 
       } 
      } 
     } 
     return true; 
    } 


    /** 
    * Method to register a service 
    */ 
    public void registerService(CustomService service) { 
     services.add(service); 
    } 

    /** 
    * Method to remove a service 
    */ 
    public void removeService(CustomService service) { 
     services.remove(service); 
    } 

} 

我々は(cancelServicesを呼び出してサービスを停止する場合)。これは状態をキャンセルするように設定します私はcalculateMethod()でこれをチェックしてから、戻り直前に状態を停止に設定します(実際にスレッドを終了します)。あなたの問題へ

if(task.isCancelled()) { 
     service.setStopped(true); 
     return null; 
} 
+0

デシベルになぜ複数の呼び出し? 1回のコールで必要な情報をすべて手に入れてみませんか? – Sedrick

+0

それはDBから受け取ったものに基づいていくつかの計算を行い、その結果を使ってDBから他のものを得るためです。私は変更できないビジネスロジックです。 – user3552551

+1

'task'が終了するまで、dbへの別の呼び出しをトリガーする' Nodes'をすべて無効にします。 – Sedrick

答えて

0

ソリューションでき(あなたが話題hereの詳細を見つけることができます)世紀前に廃止されましたThead.stop()、こと。 (Taskの文脈において)Task.cancel()と同じである、Thread.interrupt()を使用することが提案されている同様の動作を実装する

ソリューション:

  • はあなたのcalculationMethodisCancelled()でチェックを入れます。
  • アンダーリング操作を他のThreadで中断しようとします。

2番目の解決策はおそらくあなたが探しているものですが、calculationMethodの実際のコード(共有することはできません)に依存します。長いデータベース操作を殺すため

一般的な例(本のすべてが別のスレッドから実行されます):

  • データベースへの接続を強制終了します(データベースが切断の操作を殺すために十分にスマートであると仮定して、データベースのロックを解除します)。
  • 操作を強制終了するようデータベースに依頼してください(例:kill <SPID>)。

EDIT:

私は私の答えを書いたとき、あなたはSQLiteのにデータベースを指定したことをことを見ていませんでした。だから、SQLiteのためのソリューションを指定します接続を殺し

  • は(私はあなたがあなたのためにJDBCを使用していると仮定しますあなたのJavaのSQLiteのインタフェース
3

sqlite3_interruptの同等のため

  • ルックを助けにはなりません

    私はすべてのデータベースアクセスをシングルトンクラスに集約します。これは、最後のPreparedStatementが現在のクエリを1つで実行し続けることになりますeスレッドExecutorServiceisQueryRunning()runQuery()cancelQuery()などのシングルトンインスタンスには、​​というようなものを尋ねることができるので、計算をキャンセルする必要があるときはいつでもメッセージを表示してキャンセルし、新しいものを開始することができます。

    ような何か(ヌルチェックとcatch (SQLException e)ブロックを追加):

    public class DB { 
    
        private Connection cnx; 
        private PreparedStatement lastQuery = null; 
        private ExecutorService exec = Executors.newSingleThreadExecutor(); // So you execute only one query at a time 
    
        public synchronized boolean isQueryRunning() { 
         return lastQuery != null; 
        } 
    
        public synchronized Future<ResultSet> runQuery(String query) { 
         // You might want to throw an Exception here if lastQuery is not null (i.e. a query is running) 
         lastQuery = cnx.preparedStatement(query); 
         return exec.submit(new Callable<ResultSet>() { 
          public ResultSet call() { 
           try { 
            return lastQuery.executeQuery(); 
           } finally { // Close the statement after the query has finished and return it to null, synchronizing 
            synchronized (DB.this) { 
             lastQuery.close(); 
             lastQuery = null; 
            } 
           } 
          } 
          // Or wrap the above Future<ResultSet> so that Future.cancel() will actually cancel the query 
        } 
    
        public synchronized void cancelQuery() { 
         lastQuery.cancel(); // I hope SQLite supports this 
         lastQuery.close(); 
         lastQuery = null; 
        } 
    
    } 
    
  • 0

    たぶんあなたが追加し、その後、スレッドのrunメソッド(たぶんcalculationMethod)で、スレッドインスタンスT1、t1.interrupt()メソッドを呼び出すことができます条件文WALモード(先行書き込みログ)で

    public void run() { 
         while (!Thread.currentThread().isInterrupted()) { 
          try { 
           // my code goes here 
          } catch (IOException ex) { 
           log.error(ex,ex) 
          } 
         } 
        } 
    
    0

    あなたはSQLiteのデータベースに並列に

    を多くのクエリを行うことができますWAL読者は作家をブロックしないように、より同時実行性を提供し、 作家がブロックされません。読者。読み書きは同時に に進むことができます。

    https://sqlite.org/wal.html

    おそらく、これらのリンクは、あなたに興味のある:

    https://stackoverflow.com/a/6654908/1989579 https://groups.google.com/forum/#!topic/sqlcipher/4pE_XAE14TY https://stackoverflow.com/a/16205732/1989579

    関連する問題