最近有个需求处理数据,需要写个java程序来做,想着可以用多线程来加快处理数据,所以用到了threadLocal和线程池,但是对线程池的理解不够深入所以遇到了一个bug。
代码(简写):
public class Message {
private static ThreadLocal<Connection> threadLocal = new ThreadLocal(){
@Override
protected Connection initialValue() {
return HiveClient.getConnection();
}
};
public static void main(String[] args) {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(5,20,20, TimeUnit.SECONDS,new LinkedBlockingQueue<Runnable>(100));
for (int i = 0; i< 10 ;i ++){
threadPoolExecutor.execute(new Handle(threadLocal));
}
}
}
public class Handle implements Runnable {
private ThreadLocal<Connection> threadLocal;
private Connection connection;
public Handle(ThreadLocal<Connection> threadLocal){
this.threadLocal = threadLocal;
}
public void handle(String sql){
connection = threadLocal.get();
try{
PreparedStatement prepareStatement = connection.prepareStatement(sql);
prepareStatement.execute();
//处理数据
}catch (Exception e){
}finally {
try {
connection.close(); //此处不能关闭连接,否则会抛出异常。
} catch (SQLException e) {
e.printStackTrace();
}
}
}
public void run(){
String sql = "xxxxxx";
handle(sql);
}
}
问题:
在处理完五个线程后,就抛出 SASL authentication not complete 。
原因:
自己对线程池的理解不够深入,在handle方法里面处理完数据后把connection ·给关闭了,而线程池是一直复用core 数量的线程的,处理完任务之后并不会回收,而threadLocal 是和线程绑在一起的,所以下一个任务复用线程池的时候用 threadLocal.get() 方法拿到的是上一个任务已经关闭连接的connection, 所以会抛出异常。
ThreadLocal 解析:
首先看下ThreadLocal 的get方法:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
调用了getmap方法拿到 ThreadLocalMap 类,然后从ThreadLocalMap类里面拿到 ThreaLocal 的实例名 为key的value,如果map为空,则设置初始值并且返回。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
T value = initialValue(); 这个初始值就是在new ThreadLocal 时候设置的。看下getMap方法。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
ThreadLocal.ThreadLocalMap threadLocals = null;
最后threadLocal类里面有一个ThreadLocalMap 的类,getMap方法就是拿到这个类。
线程池解析:
过一段时间补上。