自己理解了一下,多处理系统下,过少的线程会浪费处理器资源,多的线程产生不必要的上下文切换反而降低效率。线程池是为了在处理大量的并发的小任务时创建合适数量的线程,减少了创建和销毁线程的开销,同时也能避免造成高额的上下文切换代价,通常应用于web服务器处理大量请求。
虽然java提供了4种常用的线程池实现,不过学习应当知其然更要知其所以然,所以还是先研究一番线程池的实现原理。
new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, TimeUnit,runnableTaskQueue, handler);
从构造方法可以看出不少端倪
- corePoolSize 线程池基本大小,一开始也不太懂基本大小什么鬼,查了一下是当线程池小于这个大小的时候即使有空闲线程,也创建新的线程来执行新加入的任务。其实相当于一个初始化,把线程池控制在一个合适的大小以优化资源的使用。如果调用prestartAllCoreThreads方法,线程池会提前创建并启动所有基本线程。
- maximumPoolSize 设定最多线程数量。
- keepAliveTime,TimeUnit 线程存活时间,后者是单位。
- runnableTaskQueue 这个可以看出java实现的线程池维护了一个任务队列,有多种阻塞队列可以选择。
- handler 当线程池任务满了的时候对新添加的任务的处理策略。
之后用网上找到的一张图可以清楚地看到线程池的工作机制,调用execute(runnable)提交任务的时候,线程池按照下图流程工作
最后按照自己的理解总结一下,其实线程池本质是对象池,是对 对象的复用。这里的对象是指线程。线程池管理这些线程对象,并复用它们按照一定的策略来不断完成被添加到队列里的任务。联想到连接池,其实就是复用的对象变成了数据库的connection而已。
java实现的4中常用线程池,封装在Excutors中
Executors.newCachedThreadPool();
创建一个不限制大小的线程池,当无线程空闲时会创建新的线程处理任务
public class ThreadPoolTest {
public static void main(String[] args) {
ExecutorService myThreadPool = Executors.newCachedThreadPool();
for (int i = 0; i < 100; i++) {
myThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
}
}
Executors.newFixedThreadPool(int n);
创建最大为n的线程池。当没有线程空闲时任务会等待。
ExecutorService myThreadPool = Executors.newFixedThreadPool(10);
for (int i = 0; i < 100; i++) {
myThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
newScheduledThreadPool
这个线程池可以周期性的执行一批任务,类似批处理,每隔多长时间跑一次,不过是多线程的。
ExecutorService myThreadPool = Executors.newScheduledThreadPool(5);//设定线程池是5个线程的,每个周期执行执行5个任务,如果任务数量多于5个,那么需要等待。
for (int i = 0; i < 5; i++) {
((ScheduledExecutorService) myThreadPool).scheduleAtFixedRate(new Runnable() {//调用这个方法执行周期的任务。
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);//为了体现等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, 0, 5, TimeUnit.SECONDS);//0s开始,每隔5s跑一次。for循环生成5个任务。所以每隔5s输出5个线程的名字一次,如果这里生成6个任务,第六个需要等待前面的完成。
}
newSingleThreadExecutor
单线程的线程池,保证任务队列fifo。
ExecutorService myThreadPool = Executors.newSingleThreadExecutor();
for (int i = 0; i < 5; i++) {
myThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
});
最后实践一下利用线程池来访问数据库做一些操作,简单写了个demo,因为3.21哪个服务器访问不了,所以自己找了一些数据,统计一个数据库下所有表的age字段的总值。 对每个表建立一个统计查询的任务,交到线程池处理。最终输出总的结果。 由于数据比较少,运行时间没什么参考意义,等明天服务器可以访问了再测试下。
package test;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class SqlThreadPoll {
static ConnectionFactory cf = new ConnectionFactory();
static ExecutorService pool = Executors.newFixedThreadPool(10);
public static void main(String[] args) {
Connection conn = cf.getConnection();
String sql = "select table_name from information_schema.tables where table_schema='test' "; //选取所有表
try {
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
while (rs.next()) {
pool.execute(new Work(rs.getString("table_name"), cf)); //每个表建立一个任务提交给线程池
}
st.close();
pool.shutdown();
while (true) {
if (pool.isTerminated()) {//等待线程池任务全部结束
System.out.println(Source.result);
break;
}
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
} catch (SQLException e) {
e.printStackTrace();
}
}
}
class Source {
static long result;
public static void add(long i) {
synchronized (Source.class) {
Source.result += i;
}
}
}
class Work implements Runnable {
private final ConnectionFactory cf;
private String sql = "select sum(age) from "; //对每个表计算年龄总数值
Work(String table_name, ConnectionFactory cf) {
this.cf = cf;
sql = sql + table_name;
}
@Override
public void run() {
try {
Connection conn = cf.getConnection();
Statement st = conn.createStatement();
ResultSet rs = st.executeQuery(sql);
int i = 0;
if (rs.next())
i = rs.getInt(1);
st.close();
System.out.println(i);
Source.add(i);
} catch (SQLException e) {
e.printStackTrace();
}
}
}
class ConnectionFactory { //单例模式返回一个数据库连接, 或者用连接池会快些?
private final static String url = "jdbc:mysql://localhost/test";
private final static String driver = "com.mysql.jdbc.Driver";
private final static String user = "root";
private final static String passwd = "root";
private static Connection conn;
static {
try {
Class.forName(driver);
conn = DriverManager.getConnection(url, user, passwd);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
}
public Connection getConnection() {
return conn;
}
}