package book.thread.pool;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.AtomicInteger;
public class JDBCTools {
private static final AtomicInteger nextId = new AtomicInteger(0);
public static String HOST = "localhost";
public static String PORT = "3306";
public static String DATABASE_NAME = "jpress";
public static String USER_NAME = "root";
public static String PASSWORD = "123456";
public static void main(String[] args) throws Exception {
executeSql();
executeSqlByThreadPool(getRunnable());
}
/**
* 默认执行,这种方式如果,Thread链接数设置太多,大于200左右,一般就会挂掉,即使设置了mysql的最大链接数
* show variables like '%max_connections%';
set GLOBAL max_connections = 16384;
*/
private static void executeSql() {
for(int i= 0 ;i< 100; i++) {
Thread thread = new Thread(getRunnable());
thread.start();
}
}
/**
* 使用线程池的方式来解决此问题, 声明100个线程,1000个 insert的任务,然后每次执行100个任务,这样依次执行
*
*/
private static void executeSqlByThreadPool(Runnable runnable) {
ExecutorService pool = Executors.newFixedThreadPool(100);
for (int i = 0; i < 100; i++) {
// 可以执行Runnable对象或者Callable对象代表的线程
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
pool.submit(runnable);
}
}
/**
* 策略3,分库分表策略
* @return
*/
private static Runnable getRunnable() {
return ()->{
try {
System.out.println(Thread.currentThread().getName() +"sql session");
// JDBCTools.query("select * from Test ");
JDBCTools.execute("insert Test(name) VALUE ('"+ nextId.getAndIncrement() + "')");
} catch (Exception e) {
e.printStackTrace();
}
};
}
/**
* 获取数据库连接
* @return 数据库连接
*/
public static Connection getConn() throws Exception {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("成功加载驱动");
String url = "jdbc:mysql://" + HOST + ":" + PORT + "/" + DATABASE_NAME + "?user=" + USER_NAME + "&password=" + PASSWORD + "&testOnBorrow=true ";
System.out.println(url);
Connection connection = DriverManager.getConnection(url);
System.out.println("成功获取连接");
return connection;
}
/**
* 关闭资源
*/
public static void closeResource(Connection conn, Statement st, ResultSet rs) {
if (rs != null) {
try {
rs.close();
} catch (SQLException e) {
// TODO 处理异常
e.printStackTrace();
}
}
if (st != null) {
try {
st.close();
} catch (SQLException e) {
// TODO 处理异常
e.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// TODO 处理异常
e.printStackTrace();
}
}
System.out.println("成功关闭资源");
}
/**
* 查询SQL
* @param sql 查询语句
* @return 数据集合
* @throws SQLException
*/
public static List<Map<String, String>> query(String sql) throws Exception {
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
List<Map<String, String>> resultList = null;
try {
connection = JDBCTools.getConn();
statement = connection.createStatement();
resultSet = statement.executeQuery(sql);
System.out.println("SQL : " + sql);
ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
int columnCount = resultSetMetaData.getColumnCount();
String[] columnNames = new String[columnCount + 1];
for (int i = 1; i <= columnCount; i++) {
columnNames[i] = resultSetMetaData.getColumnName(i);
}
resultList = new ArrayList<Map<String, String>>();
resultSet.beforeFirst();
while (resultSet.next()) {
Map<String, String> resultMap = new HashMap<String, String>();
for (int i = 1; i <= columnCount; i++) {
resultMap.put(columnNames[i], resultSet.getString(i));
}
resultList.add(resultMap);
}
System.out.println("成功查询数据库,查得数据:" + resultList);
} catch(Throwable t) {
// TODO 处理异常
t.printStackTrace();
} finally {
JDBCTools.closeResource(connection, statement, resultSet);
}
return resultList;
}
/**
* 执行SQL
* @param sql 执行的SQL
* @return 操作条数
*/
public static int execute(String sql) throws Exception {
Thread.sleep(2000);
Connection connection = null;
Statement statement = null;
ResultSet resultSet = null;
int num = 0;
try {
connection = JDBCTools.getConn();
statement = connection.createStatement();
num = statement.executeUpdate(sql);
System.out.println("SQL : " + sql);
System.out.println("成功操作数据库,影响条数:" + num);
// 模拟异常,用于测试事务
/*
if (1 == 1) {
throw new RuntimeException();
}
*/
} catch(Exception e) {
// 处理异常:回滚事务后抛出异常
e.printStackTrace();
// connection.rollback();
System.out.println("事务回滚");
throw e;
} finally {
JDBCTools.closeResource(connection, statement, resultSet);
}
return num;
}
}
判断线程池结束的方法为:
exe.shutdown();该方法在加入线程队列的线程执行完之前不会执行
exe.isTerminated();当shutdown()或者shutdownNow()执行了之后才会执行,并返回true
public class Test {
public static void main(String args[]) throws InterruptedException {
ExecutorService exe = Executors.newFixedThreadPool(50);
for (int i = 1; i <= 5; i++) {
exe.execute(new SubThread(i));
}
exe.shutdown();
while (true) {
if (exe.isTerminated()) {
System.out.println("结束了!");
break;
}
Thread.sleep(200);
}
}
}
一般情况下牵扯到第三方的jar包时,不要用Thread.activeCount(),因为一般默认会等于2,但是使用第三方jar包时,并不一定就是2了,如下所示,当我们使用okHttp.jar进行请求时,则出现了以下情况,共有四个,此时,则会和我们的要求不符合
activeCount-->main
activeCount-->Monitor Ctrl-Break
activeCount-->OkHttp ConnectionPool
activeCount-->Okio Watchdog