通过线程池的原理不难发现,他是使用现在空闲线程执行任务。而Threadlocal是为每个线程创建变量的副本(一个线程一套变量,线程之间数据独立)。
既然是一个线程可能执行多个任务,这时就很容易出现,一个线程+Threadlocal使用同一套变量,在执行不同的任务。这时就很容易出现变量在业务逻辑中错乱的问题。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
解决办法是:在线程执行前后,复位Threadlocal中的值。
如:ThreadPoolExecutor线程池中的其中三个方法:
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
protected void terminated() { }
大家可以测试一下,以下代码:
package java.test;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
/**
* 使用TreadPool时的ThreadLocal示例
*/
public class TestThreadPool {
private static ThreadLocal<Boolean> bol = new ThreadLocal<Boolean>();
/**
* 第二个提交的Runnable没有对TreadLocal进行set,但是已经被set过了
*/
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 1, 100, TimeUnit. SECONDS ,
new ArrayBlockingQueue<Runnable>(1), new ThreadPoolExecutor.AbortPolicy() );
// 进行set的Runnable
pool.execute(new Runnable() {
<span style="white-space:pre"> </span> @Override
<span style="white-space:pre"> </span>public void run() {
<span style="white-space:pre"> </span> <span style="white-space:pre"> </span>/** *set为true** */
<span style="white-space:pre"> </span> <span style="white-space:pre"> </span>bol.set(true );
<span style="white-space:pre"> </span>System.out.println("" + Thread.currentThread() + "seted bol:" + bol.get());
<span style="white-space:pre"> </span>}
});
// 没有set的Runnable
pool.execute(new Runnable() {
<span style="white-space:pre"> </span> @Override
<span style="white-space:pre"> </span>public void run() {
<span style="white-space:pre"> </span>System.out.println("" + Thread.currentThread() + "not seted bol:" + bol.get());
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span> });
}
}
自定义线程池:
import java.util.Random;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
public class LogThreadPool extends ThreadPoolExecutor{
public LogThreadPool(int corePoolSize, int maximumPoolSize,
long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
}
private final AtomicInteger numTasks = new AtomicInteger();//线程个数
private final AtomicLong totalTimes = new AtomicLong();//所有线程总执行时间
private ThreadLocal<Long> startTime = new ThreadLocal<Long>();
public static void main(String[] args) {
ThreadPoolExecutor pool = new LogThreadPool(0, Integer.MAX_VALUE, 60,
TimeUnit.SECONDS, new SynchronousQueue<Runnable>());
Runnable task = new Runnable(){
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"任务正在执行");
try {
int rand = new Random().nextInt(1000);
Thread.sleep(rand);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
};
int ThreadNum = 5;
for(int i=0; i<ThreadNum; i++){
pool.execute(task);
}
pool.shutdown();
}
//在执行给定线程中的给定 Runnable 之前调用的方法
@Override
protected void beforeExecute(Thread t, Runnable r) {
super.beforeExecute(t, r);
System.out.println(Thread.currentThread().getName()+"任务执行之前..");
startTime.set(System.currentTimeMillis());
}
//基于完成执行给定 Runnable 所调用的方法
@Override
protected void afterExecute(Runnable r, Throwable t) {
super.afterExecute(r, t);
long endTime = System.currentTimeMillis();
long tasktime = endTime - startTime.get();
numTasks.incrementAndGet();
System.out.println(Thread.currentThread().getName()+"任务执行之后运行毫秒数:"+tasktime);
totalTimes.addAndGet(tasktime);
}
//当 Executor 已经终止时调用的方法
@Override
protected void terminated() {
super.terminated();
System.out.println("任务全部完成.......");
System.out.println("共有几个线程执行:"+numTasks);
System.out.println("共运行了几毫秒:"+totalTimes.get());
}
/**
* 运行结果:
* pool-1-thread-1任务执行之前..
pool-1-thread-1任务正在执行
pool-1-thread-2任务执行之前..
pool-1-thread-2任务正在执行
pool-1-thread-4任务执行之前..
pool-1-thread-4任务正在执行
pool-1-thread-3任务执行之前..
pool-1-thread-3任务正在执行
pool-1-thread-5任务执行之前..
pool-1-thread-5任务正在执行
pool-1-thread-1任务执行之后运行毫秒数:47
pool-1-thread-3任务执行之后运行毫秒数:407
pool-1-thread-4任务执行之后运行毫秒数:469
pool-1-thread-2任务执行之后运行毫秒数:500
pool-1-thread-5任务执行之后运行毫秒数:703
任务全部完成.......
共有几个线程执行:5
共运行了几毫秒:2126
*/
}