设为首页 加入收藏

TOP

面试再也不怕问ThreadLocal了(二)
2023-08-06 07:49:47 】 浏览:85
Tags:ThreadLocal
到消息id,再发送出去。当然,实际还要考虑发送失败的情况,通过定时任务补偿。这就是本地消息表的一种实现思路,ThreadLocal存储了消息id,在事务提交后,再从ThreadLocal取出来发送消息。
首先说明,如下写法是不可取的,原因有:1.如果事务commit失败,mq还是发出去了 2.导致事务时间变长,事务内不宜处理其它耗时逻辑,如发送mq,调用接口等。

@Transactional
public void register() {
    //插入数据
    User user = new User();
    userMapper.insert(user);

    //发送消息,处理其它事情
    mq.send(topic, user.getId());
}

改写如下:

@Transactional
public void register() {
    //插入数据
    User user = new User();
    userMapper.insert(user);
    UserMsg userMsg = new UserMsg();
    userMsgMapper.insert(userMsg);

    //不使用整个user对象,只存个id占用内存较少,user对象可以及时被回收
    threadLocal.set(user.getId);

    //注册回调
    transCallbackService.afterCommit(() -> {
		mqHandleService.handleUserRegister();
	});
}

@Service
class TransCallbackService {
	public void afterCommit(Runnable runnable) {
		if (TransactionSynchronizationManager.isActualTransactionActive()) {
			TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronizationAdapter() {
				@Override
				public void afterCommit() {
					runnable.run();
				}
			});
		}
	}
}

@Service
class MqHandleService {
	public void handleUserRegister() {
        	//从threadlocal获取id,再处理各种事情
		mq.send(topic, threadLocal.get());
	}
}

弱引用问题

java中对象引用有几种类型:强引用,弱引用,软引用和虚引用,它们的区别主要跟gc有关。

强引用:通常我们写的代码都是强引用,如:User user = new User(); user就是一个强引用,它指向了内存一块区域,只要user还是可达的,那么gc就不会回收对应的内存。如果user的作用域非常长,而且后面它又没有用到了,可以将它设置为null,这样gc可以快点回收对应的内存,当然现在jvm比较智能,可以自动分析完成这个事情。还有一个注意事项是,如果对象被如一个全局的HaspMap引用着,那么即使设置为null或者指向它的变量不可达了,它也不会被回收,如:

User user = new User();
HashMap map = new HashMap(); //被map引用着,map可达就不会被回收
map.put(user, 1);
user = null;

弱引用:如果一个对象只被弱引用对象引用着,那么它会在下一次gc被回收,弱引用使用WeakReference 类。如:

User user = new User();
WeakReference weakReference = new WeakReference(user);
user = null; 
HashMap hashMap = new HashMap();
hashMap.put(weakReference,1);
System.gc();

当执行完user=null后,其对象内存区域就没有强引用指向它了,只有一个弱引用对象weakReference。接着执行gc,user原本指向的内存就会被回收。此时我们执行weakReference.get()将拿到一个null。 从这里可以看到如果使用弱引用,假设我们忘记从HashMap移除不需要的元素,它也会再下一次gc时被回收,防止内存泄漏。

软引用:在内存充足的条件下,不会被回收,只要在内存不足时才会被回收。
虚引用:随时可能被同时,主要用于跟踪gc,在对象被gc后会收到一个通知。

对于ThreadLocal来说,它里面的Entry继承了WeakReference ,会把key也就是ThreadLocal对象设置为弱引用。那为什么要这么做呢?
上面的例子我们刚提到,当你忘记remove的时候,使用弱引用可以防止内存泄漏,ThreadLocal也是出于这目的。假设key不是弱引用,开发者忘记remove,那么key就发生内存泄漏,只能等到Thread对象销毁时才回收,在一些使用线程池的场景下,Thread会一直复用,就会导致内存一直回收不了。

    	public void test() {
		inner();
		System.gc();	
        	//Thread.currentThread.threadLocals	        
	}

	private void inner() {
		TestClass testClass = new TestClass();
		testClass.set(new User());
	}

	class TestClass {
		
        	ThreadLocal threadLocal = new ThreadLocal();

		public void set(Object value) {
			threadLocal.set(value);
		}
	}

    	//更简单的例子
    	public void test() {
		ThreadLocal threadLocal = new ThreadLocal();
        	threadLocal.set(new User());
        	threadLocal = null;
		System.gc();	
        	//Thread.currentThread.threadLocals	        
    	}

如上代码,往ThreadLocal放了一个User对象,此时ThreadLocalMap就维护一个key为threadLocal,value为User的Entry,当inner方法执行完,threadLocal已经不可达,但它的内存区域还被Entry引用着,并且没法再访问到,如果是强引用,就出现内存泄漏。如果是弱引用,在gc后,我们观察Thread.currentThread.threadLocals就可以发现,它的referent变成了null,被回收了。但作为value的User对象是强引用,不会被回收。到这里有些面试官就会问,为什么value不也设置为弱引用呢?

如下代码:

    	public void test() {
		TestClass test
首页 上一页 1 2 3 4 下一页 尾页 2/4/4
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇MQ消息队列篇:三大MQ产品的必备.. 下一篇找出乱序数组第k大的数字(堆排序..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目