PageHelper中遇到的ThreadLocal小坑

4 篇文章 0 订阅

最近写代码刚好碰到ThreadLocal的小坑,顺便学习了一波ThreadLocal,拿出来分享一下

ThreadLocal什么时候会出现线程不安全的情况呢?

我总结了两种情况

1.记录在 ThreadLocal 中的是一个线程共享的外部对象

https://www.cnblogs.com/qilong853/p/5982878.html

这边文章讲的很好,我就不复制黏贴了,ThreadLocal中保存的是Object对象的一个引用,这样的话,当有其他线程对这个引用指向的对象做修改时,当前线程Thread对象中保存的值也会发生变化

	public class ThreadLocalTest implements Runnable {
	    private Integer count = 0;
		// 一个普通的对象
	    private NumberClass numberClass = new NumberClass();
	    private ThreadLocal<NumberClass> threadLocal = new ThreadLocal();
	    @Override
	    public void run() {
	        numberClass.setNum(++count);
	        threadLocal.set(numberClass);
	        try {
	            Thread.sleep(1000);
	        } catch (InterruptedException e) {
	            e.printStackTrace();
	        }
	        System.out.println("[Thread-" + Thread.currentThread().getId() + "]"+threadLocal.get().getNum());
	
	    }
	}

	 public static void main(String[] args){
	        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(5);
	       ThreadLocalTest thread = new ThreadLocalTest();
	        for (int i =0;i<5;i++ ){
	            fixedThreadPool.execute(thread);
	        }

	//                [Thread-12]5
	//                [Thread-15]5
	//                [Thread-14]5
	//                [Thread-11]5
	//                [Thread-13]5

	// 执行完5个线程中的threadLocal中的值相同
	
	    }

2.引入线程池,线程复用

如果在一个线程被使用完准备回放到线程池中之前,我们没有对记录在数据库中的数据执行清理,那么这部分数据就会被下一个复用该线程的业务看到

	public class ThreadLocalTest2 implements Runnable {
	//    private Integer count = 0;
	    private ThreadLocal<List> threadLocal = new ThreadLocal();
	    @Override
	    public void run() {
	//        Integer count = 0;
	        List<Long> list = threadLocal.get();
	        if (list == null){
	            list = new ArrayList<>();
	            System.out.println("[Thread-" + Thread.currentThread().getId() + "]  init threadLocal");
	        }
	        list.add((long) (1+Math.random()*(10)));
	        threadLocal.set(list);
	        List list2 = threadLocal.get();
	        System.out.println("[Thread-" + Thread.currentThread().getId() + "]"+list);
	
	    }
	}

	  public static void main(String[] args){
	        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
	        ThreadLocalTest2 thread = new ThreadLocalTest2();
	        for (int i =0;i<5;i++ ){
	            fixedThreadPool.execute(thread);
	        }
	
	// 执行结果
	//                [Thread-11]  init threadLocal
	//                [Thread-12]  init threadLocal
	//                [Thread-11][9]
	//                [Thread-12][5]
	//                [Thread-11][9, 1]
	//                [Thread-12][5, 9]
	//                [Thread-11][9, 1, 4]
	
	    }

3.自己踩到的坑

首先碰到的坑是这样的,首先这个selectList是mybatisPlus的方法,执行到这个方法的时无缘无故调用了分页的相关sql,而这个方法里并没有进行分页(pageHelper)的配置。当时我们就想到是线程串了,当前线程使用了其他线程的分页配置。但是我们通过观察PageHelper的源码发现,

    protected static final ThreadLocal<Page> LOCAL_PAGE = new ThreadLocal<Page>();

配置是放在ThreadLocal里面的,而且分页查询执行完毕,会调用销毁ThreadLocal的方法,不会出现第二种问题,

    public static void clearLocalPage() {
            LOCAL_PAGE.remove();
    }

并且存的Page对象是new的变量,也不会出现第一种问题。
正常情况不会出现线程不安全的情况。

	public static <E> Page<E> startPage(int pageNum, int pageSize, boolean count, Boolean reasonable, Boolean pageSizeZero) {
	      Page<E> page = new Page<E>(pageNum, pageSize, count);
        	 ...
    }

坑在这里
图
写代码的时候一开始设置了pageHelper,后面进行了if语句,没有执行下面的sql,导致page信息没有被销毁,当这个线程被复用的时候,就会以为要分页了

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值