ThreadLocal use case scenarios

Benpian is " ThreadLocal that point thing ," the sequel, if you do not fancy one, it is a little loss. If you missed this one, thanks to that is even greater.

I still maintain the consistent Style, with a Demo speak for it. Users put forward a demand: When the modified product prices when the need to record the operation log, when to do what.

Presumably this case, as long as the application system has done little friends, should come across it? Nothing less than the database on two tables: product and log, with two SQL statements should be able to solve the problem:

update product set price = ? where id = ?
insert into log (created, description) values (?, ?)

But! To ensure that these two SQL statements must be submitted in the same transaction, otherwise it is possible to submit an update, but the insert did not commit. If such a thing happens, we will certainly be pointing to the user's nose rave: "Why do prices change, but do not see what time it changed?."

Smart after I received the demand, do this:

First of all, I wrote a DBUtil tool, a package of common database operations: 

public class DBUtil {
    // 数据库配置 private static final String driver = "com.mysql.jdbc.Driver"; private static final String url = "jdbc:mysql://localhost:3306/demo"; private static final String username = "root"; private static final String password = "root"; // 定义一个数据库连接 private static Connection conn = null; // 获取连接 public static Connection getConnection() { try { Class.forName(driver); conn = DriverManager.getConnection(url, username, password); } catch (Exception e) { e.printStackTrace(); } return conn; } // 关闭连接 public static void closeConnection() { try { if (conn != null) { conn.close(); } } catch (Exception e) { e.printStackTrace(); } } }

Which engage in a static Connection, which erupted like a database connection operations, and Niubi it!

Then, I define an interface for logical layer to be called:

public interface ProductService {

    void updateProductPrice(long productId, int price); }

According to user demand put forward, I think this interface is completely sufficient. The price to update the corresponding productId Product, and then inserted into a data log table.

In fact, the business logic is not very complicated, so I quickly completed the implementation class ProductService interface:

public class ProductServiceImpl implements ProductService { private static final String UPDATE_PRODUCT_SQL = "update product set price = ? where id = ?"; private static final String INSERT_LOG_SQL = "insert into log (created, description) values (?, ?)"; public void updateProductPrice(long productId, int price) { try { // 获取连接 Connection conn = DBUtil.getConnection(); conn.setAutoCommit(false); // 关闭自动提交事务(开启事务) // 执行操作 updateProduct(conn, UPDATE_PRODUCT_SQL, productId, price); // 更新产品 insertLog(conn, INSERT_LOG_SQL, "Create product."); // 插入日志 // 提交事务 conn.commit(); } catch (Exception e) { e.printStackTrace(); } finally { // 关闭连接 DBUtil.closeConnection(); } } private void updateProduct(Connection conn, String updateProductSQL, long productId, int productPrice) throws Exception { PreparedStatement pstmt = conn.prepareStatement(updateProductSQL); pstmt.setInt(1, productPrice); pstmt.setLong(2, productId); int rows = pstmt.executeUpdate(); if (rows != 0) { System.out.println("Update product success!"); } } private void insertLog(Connection conn, String insertLogSQL, String logDescription) throws Exception { PreparedStatement pstmt = conn.prepareStatement(insertLogSQL); pstmt.setString(1, new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date())); pstmt.setString(2, logDescription); int rows = pstmt.executeUpdate(); if (rows != 0) { System.out.println("Insert log success!"); } } }

Readability of the code is pretty good, right? Here I used the advanced features of JDBC Transaction. After secretly glad about it, I think it is not necessary to write a client to test the results are not what I want it? So I lazy, directly increases a main () method in ProductServiceImpl in:

public static void main(String[] args) { ProductService productService = new ProductServiceImpl(); productService.updateProductPrice(1, 3000); }

I want to productId price of the product 1 modified to 3000. So I ran the program again, the console output:

Update product success!
Insert log success!

It should be right. As a professional programmer. To be sure, I have to look in to the database. Yes! product table record corresponding to the update, log table also insert a record. This allows you to ProductService interfaces delivered to someone else to call.

Hours passed, QA sister began to scold me: "I rely on my only simulated 10 requests, how you hung up saying this interface is the database connection closed!?!."

Hear the cry, let me whole body trembled, immediately interrupted my little video, quickly open the IDE, we found this ProductServiceImpl the implementation class. Bug does not seem right? But now I can not give her any response, I did a little afraid of her.

I suddenly remembered that she is a simulation tool, which is a simulation of multiple threads! I myself can simulate ah, so I wrote a thread class:

public class ClientThread extends Thread { private ProductService productService; public ClientThread(ProductService productService) { this.productService = productService; } @Override public void run() { System.out.println(Thread.currentThread().getName()); productService.updateProductPrice(1, 3000); } }

I use this thread to call ProduceService way to see is not a problem. At this point, I still have to amend the main () method:

// public static void main(String[] args) {
//     ProductService productService = new ProductServiceImpl();
//     productService.updateProductPrice(1, 3000);
// }
    
public static void main(String[] args) { for (int i = 0; i < 10; i++) { ProductService productService = new ProductServiceImpl(); ClientThread thread = new ClientThread(productService); thread.start(); } }

I also simulated 10 threads, and I do not believe that evil!

Operating results and I'm really faint, very faint:

Thread-1
Thread-3
Thread-5
Thread-7
Thread-9
Thread-0
Thread-2
Thread-4
Thread-6
Thread-8
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: No operations allowed after connection closed.
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:39)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:27)
at java.lang.reflect.Constructor.newInstance(Constructor.java:513)
at com.mysql.jdbc.Util.handleNewInstance(Util.java:411)
at com.mysql.jdbc.Util.getInstance(Util.java:386)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:1015)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:989)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:975)
at com.mysql.jdbc.SQLError.createSQLException(SQLError.java:920)
at com.mysql.jdbc.ConnectionImpl.throwConnectionClosedException(ConnectionImpl.java:1304)
at com.mysql.jdbc.ConnectionImpl.checkClosed(ConnectionImpl.java:1296)
at com.mysql.jdbc.ConnectionImpl.commit(ConnectionImpl.java:1699)
at com.smart.sample.test.transaction.solution1.ProductServiceImpl.updateProductPrice(ProductServiceImpl.java:25)
at com.smart.sample.test.transaction.ClientThread.run(ClientThread.java:18)

I rely on! Even being given in a multithreaded environment, it really is a database connection is closed. How it happened? I was lost in thought. So I Copy the error message a phrase, in Baidu, Google, as well as in the OSC are looking for, it is a strange answer.

I suddenly remembered, since it is a relationship with the Connection, then I will focus on the examination Connection relevant bar code. Connection is not static it should not be? I had designed mainly to allow the static of static methods DBUtil access more convenient, with a static variable to hold the Connection also improves the performance ah. How do it?

So I saw the OSC very popular article " ThreadLocal that point thing ", and finally just let me know! The original make each thread has its own connection, rather than sharing the same connection, or the thread 1 is likely to close the connection thread 2, so the thread 2 on the error. It must be so!

I hasten to reconstruct the DBUtil:

public class DBUtil {
    // 数据库配置 private static final String driver = "com.mysql.jdbc.Driver"; private static final String url = "jdbc:mysql://localhost:3306/demo"; private static final String username = "root"; private static final String password = "root"; // 定义一个用于放置数据库连接的局部线程变量(使每个线程都拥有自己的连接) private static ThreadLocal<Connection> connContainer = new ThreadLocal<Connection>(); // 获取连接 public static Connection getConnection() { Connection conn = connContainer.get(); try { if (conn == null) { Class.forName(driver); conn = DriverManager.getConnection(url, username, password); } } catch (Exception e) { e.printStackTrace(); } finally { connContainer.set(conn); } return conn; } // 关闭连接 public static void closeConnection() { Connection conn = connContainer.get(); try { if (conn != null) { conn.close(); } } catch (Exception e) { e.printStackTrace(); } finally { connContainer.remove(); } } }

Connection ThreadLocal I put in, so it is isolated between each thread will not interfere with each other.

In addition, the getConnection () method, first (that is, connContainer in) Connection from ThreadLocal get in, and if not, to create a connection through JDBC, and finally the creation of a good connection into this ThreadLocal. ThreadLocal can be seen as a container, that does not leave.

Likewise, I also closeConnection () method to do the reconstruction, starting with acquiring Connection container, got to close out, it finally fell remove it from the container to keep clean container.

This time should be okay? Once again, I run main () method:

Thread-0
Thread-2
Thread-4
Thread-6
Thread-8
Thread-1
Thread-3
Thread-5
Thread-7
Thread-9
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!
Update product success!
Insert log success!

I go! Finally solved, QA sister, you should be smiling at me about it?

 

https://my.oschina.net/huangyong/blog/159725?p=6

Guess you like

Origin www.cnblogs.com/twoheads/p/11345689.html