在线程池(ExecutorService)中使用Threadlocal时,需要注意他的复位操作

通过线程池的原理不难发现,他是使用现在空闲线程执行任务。而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
 */
}


线程池使用ThreadLocal是为了在多线程环境下,每个线程都能够独立地访问自己的变量副本,而不会受到其他线程的影响。ThreadLocal提供了一种线程局部变量的机制,使得每个线程都可以维护自己的变量副本。 在使用线程池,我们可以通过在任务执行之前将需要共享的变量设置到ThreadLocal,然后在任务执行期间获取该变量。这样就可以保证每个线程都能够独立地访问自己的变量副本。 下面是一个使用ThreadLocal的示例代码: ```java import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class ThreadPoolExample { private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(); public static void main(String[] args) { ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 10; i++) { final int index = i; executorService.execute(() -> { // 设置线程局部变量的值 threadLocal.set(index); System.out.println("Thread " + Thread.currentThread().getId() + ", value: " + threadLocal.get()); // 清除线程局部变量的值 threadLocal.remove(); }); } executorService.shutdown(); } } ``` 在上述代码,我们创建了一个固定大小为5的线程池,并通过循环提交了10个任务。在每个任务,我们将任务的索引存储到ThreadLocal,并在任务执行期间获取该值并打印出来。最后,我们需要记得在任务执行结束后,清除ThreadLocal的值,以防止内存泄漏。 通过使用ThreadLocal,每个线程都能够独立地访问自己的变量副本,从而避免了线程安全的问题,并且能够有效地利用线程池来处理多个任务。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值