Advertisement
Guest User

Untitled

a guest
Mar 25th, 2019
135
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 6.59 KB | None | 0 0
  1. import com.mysql.jdbc.MySQLConnection;
  2. import java.sql.*;
  3. import java.util.concurrent.TimeUnit;
  4.  
  5. /**
  6. * MySQL 默认可重复读RR.
  7. * 两个线程T1,T2 分别执行一个业务单元.
  8. * T1中先 UPDATE test1 set field1='AA' where id=1, 然后 select field1 from test1 where id=1就应该返回 AA.
  9. * T2同理, set BB 后,select出的也应该是 BB .
  10. * <p>
  11. * 但这是建立在两个业务单元在不同事务中执行时,如果多线程同时操作统一connection,(多线程不是必要条件,只是容易理解而已),
  12. * MySQL的协议决定了两个业务单元SQL会穿插到同一个事务中,而不会爆出错误.
  13. * start transaction; sql11; sql12; commit;
  14. * start transaction; sql21; sql22; commit;
  15. * =>
  16. * start transaction; start transaction; sql11; sql21; sql12; sql22; commit; commit;
  17. * <p>
  18. * 下面例子演示了 “两个线程操作同一个connection,导致RR失效” 这一不符合直觉的例子.
  19. *
  20. * 编译:javac -cp ~/.m2/repository/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar JDBCMultiThreadDemo.java
  21. *
  22. * 执行(*nix):
  23. * 相同连接
  24. java -cp /Users/ljh/.m2/repository/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar:/Users/ljh/.m2/repository/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar:/Users/ljh/.m2/repository/ch/qos/logback/logback-core/1.1.7/logback-core-1.1.7.jar:/Users/ljh/.m2/repository/ch/qos/logback/logback-classic/1.1.7/logback-classic-1.1.7.jar:. -DuseSameConnectionSimultaneously=true JDBCMultiThreadDemo
  25. 不同连接(作为对比)
  26. java -cp /Users/ljh/.m2/repository/mysql/mysql-connector-java/5.1.41/mysql-connector-java-5.1.41.jar:/Users/ljh/.m2/repository/org/slf4j/slf4j-api/1.7.21/slf4j-api-1.7.21.jar:/Users/ljh/.m2/repository/ch/qos/logback/logback-core/1.1.7/logback-core-1.1.7.jar:/Users/ljh/.m2/repository/ch/qos/logback/logback-classic/1.1.7/logback-classic-1.1.7.jar:. -DuseSameConnectionSimultaneously=false JDBCMultiThreadDemo
  27. *
  28. * Window下path.separator 是 ;
  29. * Linux下是 :
  30. *
  31. *
  32. */
  33. public class JDBCMultiThreadDemo {
  34. public static void main(String[] args) throws SQLException, InterruptedException {
  35.  
  36. String host = "HOST:3306";
  37. String database = "x";
  38. String user = "x";
  39. String password = "x";
  40. /*
  41. CREATE TABLE `test1` (
  42. `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  43. `field1` varchar(255) NOT NULL,
  44. PRIMARY KEY (`id`),
  45. ) ENGINE=InnoDB AUTO_INCREMENT=0 DEFAULT CHARSET=latin1;
  46. INSERT INTO test1(id,field1) values (1,"default");
  47. */
  48.  
  49. String url = String.format("jdbc:mysql://%s/%s?user=%s&password=%s", host, database, user, password) +
  50. "&useUnicode=true&characterEncoding=utf8&useSSL=false&sessionVariables=sql_mode='NO_ENGINE_SUBSTITUTION'&jdbcCompliantTruncation=false&zeroDateTimeBehavior=round" +
  51. // "&logger=com.mysql.jdbc.log.StandardLogger&profileSQL=true" ; /*打印StackTrace(bug) + sql*/
  52. "&logger=com.mysql.jdbc.log.Slf4JLogger&profileSQL=true"; /*只打印 sql*/
  53.  
  54. boolean useSameConnectionSimultaneously = Boolean.getBoolean("useSameConnectionSimultaneously");
  55. Connection t1Connection = DriverManager.getConnection(url);
  56. Connection t2Connection = useSameConnectionSimultaneously
  57. ? t1Connection
  58. : DriverManager.getConnection(url);
  59.  
  60. t1Connection.setAutoCommit(false);
  61. t2Connection.setAutoCommit(false);
  62.  
  63. System.out.println("useSameConnectionSimultaneously:" + useSameConnectionSimultaneously);
  64. System.out.println("T1 ConnectionId:" + ((MySQLConnection) t1Connection).getId());
  65. System.out.println("T2 ConnectionId:" + ((MySQLConnection) t2Connection).getId());
  66.  
  67. Thread t1 = new Thread(new MyTaskUnit("T1", t1Connection, true));
  68. t1.setName("T1");
  69. t1.start();
  70.  
  71. Thread t2 = new Thread(new MyTaskUnit("T2", t2Connection, false));
  72. t1.setName("T2");
  73. t2.start();
  74.  
  75. t1.join();
  76. t2.join();
  77.  
  78. if (t1Connection != null) {
  79. t1Connection.close(); //close()会发送 rollback 被MySQL Server
  80. }
  81.  
  82. if (t2Connection != null) {
  83. t2Connection.close(); //close()会发送 rollback 被MySQL Server
  84. }
  85.  
  86. Connection connection = DriverManager.getConnection(url);
  87. Statement statement = connection.createStatement();
  88. ResultSet resultSet = statement.executeQuery("SELECT field1 FROM test1 WHERE id=1;/*query*/");
  89. while (resultSet.next()) {
  90. String field1R = resultSet.getString(1);
  91. System.out.println(String.format("最后结果:%s", field1R));
  92. }
  93.  
  94. if (connection != null) {
  95. connection.close();
  96. }
  97.  
  98. }
  99.  
  100. public static class MyTaskUnit implements Runnable {
  101.  
  102. private Connection connection;
  103. private String tName;
  104. private String expectResult;
  105. private boolean busy;
  106.  
  107. MyTaskUnit(String t, Connection connection, boolean busy) {
  108. this.connection = connection;
  109. this.tName = t;
  110. this.expectResult = "BY_" + tName + "_" + (java.time.LocalTime.now());
  111. this.busy = busy;
  112. }
  113.  
  114. @Override
  115. public void run() {
  116.  
  117. Statement t1Statement = null;
  118. try {
  119. t1Statement = connection.createStatement();
  120. t1Statement.execute(String.format("UPDATE test1 SET field1='%s' where id=1; /*%s*/ ", expectResult, tName));
  121.  
  122. if (busy) {
  123. TimeUnit.SECONDS.sleep(2);
  124. //假装2s繁忙业务, 给另外 同时操作这个Connection的线程 机会来穿插其他语句
  125. //造成本来两个 业务单元/事务 被搞入同一个事务
  126. }
  127.  
  128. //打印结果
  129. Statement statement = connection.createStatement();
  130. ResultSet resultSet = statement.executeQuery("SELECT field1 FROM test1 WHERE id=1;/*query*/");
  131. while (resultSet.next()) {
  132. String field1R = resultSet.getString(1);
  133. String desc = expectResult.equalsIgnoreCase(field1R)
  134. ? "【GOOD——看到本业务写入的值】"
  135. : "【BAD——看到别业务写入的值, connection错误用法导致,'看上去'不符合MySQL RR】";
  136. System.out.println(String.format("%s:->期望:%s,得到:%s,%s", tName, expectResult, field1R, desc));
  137. }
  138. connection.commit();
  139.  
  140. } catch (SQLException | InterruptedException e) {
  141. e.printStackTrace();
  142. }
  143. }
  144. }
  145. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement