私はConcurrentMap.putIfAbsentのように動作するSQL DBにメソッドを書き込もうとしています。ここで、 'value'はキーとして、 'id'は値として機能します。このメソッドの主な制約は、値をテーブル全体で一意に保つことです。oracle TRANSACTION_SERIALIZABLEレベルを理解しようとしています
以下はこの方法の例です。 sync.yield()
の呼び出しは、他のスレッドに制御を渡します。必要な並列スレッド実行を実現するために追加されました。
import java.sql.*;
import java.util.concurrent.atomic.AtomicInteger;
public class Main {
private static final String USER = "";
private static final String PASS = USER;
private static final String URL = "jdbc:oracle:thin:@192.168.100.160:1521:main";
private static final AtomicInteger id = new AtomicInteger();
private static final Sync sync = new Sync(1);
static Connection getConnection() throws Exception {
Class.forName("oracle.jdbc.driver.OracleDriver");
Connection c = DriverManager.getConnection(URL, USER, PASS);
c.setTransactionIsolation(Connection.TRANSACTION_SERIALIZABLE);
return c;
}
static long putIfAbsent(String value) throws Exception {
Connection c = getConnection();
PreparedStatement checkSt = c.prepareStatement("SELECT id from test WHERE value = ?");
checkSt.setString(1, value);
ResultSet rs = checkSt.executeQuery();
if (rs.next())
return rs.getLong(1);
System.out.println(Thread.currentThread() + " did not find value");
sync.yield();
long id = getId();
System.out.println(Thread.currentThread() + " prepare to insert value with id " + id);
PreparedStatement updateSt = c.prepareStatement("INSERT INTO test VALUES (?, ?)");
updateSt.setLong(1, id);
updateSt.setString(2, value);
updateSt.executeQuery();
c.commit();
c.close();
return id;
}
public static void main(String[] args) {
Runnable r =() -> {
try {
System.out.println(Thread.currentThread() + " commit success and return id = " + putIfAbsent("val"));
sync.yield();
} catch (Exception e) {
e.printStackTrace();
}
};
Thread t1 = new Thread(r);
Thread t2 = new Thread(r);
t1.start();
t2.start();
}
static long getId() {
return id.incrementAndGet();
}
}
私は空のテーブルの上にmainメソッドを実行すると、私は、このコンソール出力を得る:
Thread[Thread-0,5,main] did not find value
Thread[Thread-1,5,main] did not find value
Thread[Thread-0,5,main] prepare to insert value with id 1
Thread[Thread-0,5,main] commit success and return id = 1
Thread[Thread-1,5,main] prepare to insert value with id 2
Thread[Thread-1,5,main] commit success and return id = 2
私は最初の5行を説明することができます。しかし、第六はできません。 スレッド1が更新を実行するとき、それはSELECT id from test WHERE value = ?
に空の結果があることに依存します。この結果は、現在のDB状態と一致しません。だから、私はORA-08177: Cannot serialize access for this transaction
を期待しています。
私は(それは、スレッドのオブジェクト上のリンク参照を保持する)同期クラスのこのimpementationsを使用しています:表を作成するための
import java.util.ArrayDeque;
import java.util.HashSet;
import java.util.Queue;
import java.util.Set;
public class Sync {
private final Object lock = new Object();
private final Queue<Thread> sleepingTh = new ArrayDeque<>();
private final Set<Thread> activeTh = new HashSet<>();
private final int threads;
public Sync(int threads) {
this.threads = threads;
}
public void yield() {
final Thread ct = Thread.currentThread();
synchronized (lock) {
sleepingTh.add(ct);
activeTh.remove(ct);
if (sleepingTh.size() > threads) {
Thread t = sleepingTh.poll();
activeTh.add(t);
lock.notifyAll();
}
while (!activeTh.contains(ct)) {
try {
lock.wait();
} catch (InterruptedException e) {
}
}
}
}
public void wakeUpAll() {
synchronized (lock) {
activeTh.addAll(sleepingTh);
sleepingTh.clear();
lock.notifyAll();
}
}
}
声明:
create table test(
id number(16),
value varchar(50)
);
を私はjdk1.8.0_60、OracleのJDBCを使用10.2.0.4.0、およびOracle DB 11g2
正確な質問は何ですか? –
ORA-08177:このトランザクションのアクセスをシリアル化できません。 – osseum