Introduction and application of ThreadLocal in Java

Introduction and application of ThreadLocal in Java

brief introduction

This class provides thread local variables. These variables are different from their normal counterparts because each thread accessing a variable (through its get or set method) has its own local variable, which is independent of the initialized copy of the variable. ThreadLocal instances are usually private static fields in a class that want to associate the state with a thread (for example, user ID or transaction ID).

For example, the following classes generate local identifiers that are unique to each thread. The thread ID is the first time uniquethreadidgenerator is called It is allocated when getcurrentthreadid(), and will not be changed in subsequent calls.

import java.util.concurrent.atomic.AtomicInteger;

public class UniqueThreadIdGenerator {

 private static final AtomicInteger uniqueId = new AtomicInteger(0);

 private static final ThreadLocal < Integer > uniqueNum = 
     new ThreadLocal < Integer > () {
         @Override protected Integer initialValue() {
             return uniqueId.getAndIncrement();
     }
 };

 public static int getCurrentThreadId() {
     return uniqueId.get();
 }

} // UniqueThreadIdGenerator

Each thread maintains an implicit reference to its thread local variable copy, as long as the thread is active and the ThreadLocal instance is accessible; After a thread disappears, all copies of its thread local instances are garbage collected (unless there are other references to these copies).

ThreadLocal does not solve the problem of data sharing, but solves that multiple threads have an independent copy of local variables, which do not affect each other, and variables are usually private static fields in classes, that is, static variables.

practical application

ThreadLocal is used in loadbean definitions of XmlBeanDefinitionReader of Spring source code.

	//XML bean definition resource currently being loaded
private final ThreadLocal<Set<EncodedResource>> resourcesCurrentlyBeingLoaded =
			new NamedThreadLocal<Set<EncodedResource>>("XML bean definition resources currently being loaded");

	public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException {
		Assert.notNull(encodedResource, "EncodedResource must not be null");
		if (logger.isInfoEnabled()) {
			logger.info("Loading XML bean definitions from " + encodedResource.getResource());
		}
		//Why is it designed like this? What is the purpose of using ThreadLocal??
		//Mainly to detect whether it is cyclic loading
		Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get();
		if (currentResources == null) {
			currentResources = new HashSet<EncodedResource>(4);
			this.resourcesCurrentlyBeingLoaded.set(currentResources);
		}
		//Check whether to load circularly, that is, whether the xml configuration file import s itself
		//When parsing for the first time, the encodedResource is initialized, then the import tag is parsed, and this method is also called
		//The second time, it is found that there is already a resource file in currentResources, so it is cyclic loading and throws an exception
		if (!currentResources.add(encodedResource)) {
			throw new BeanDefinitionStoreException(
					"Detected cyclic loading of " + encodedResource + " - check your import definitions!");
		}
		try {
			InputStream inputStream = encodedResource.getResource().getInputStream();
			try {
				InputSource inputSource = new InputSource(inputStream);
				if (encodedResource.getEncoding() != null) {
					inputSource.setEncoding(encodedResource.getEncoding());
				}
				//Load BeanDefinitions
				return doLoadBeanDefinitions(inputSource, encodedResource.getResource());
			}
			finally {
				inputStream.close();
			}
		}
		catch (IOException ex) {
			throw new BeanDefinitionStoreException(
					"IOException parsing XML document from " + encodedResource.getResource(), ex);
		}
		finally {
			currentResources.remove(encodedResource);
			if (currentResources.isEmpty()) {
				this.resourcesCurrentlyBeingLoaded.remove();
			}
		}
	}


As noted, this is to detect whether the import tag in the xml file has imported itself, which is the cyclic loading problem.

When you enter this method for the first time, currentresources is null, and then set the value. At this time, the currentresources value is bound with the current thread. When you subsequently parse the import tag in xml, you will call this method. The second time, judge! currentResources. Add (encoded resource), of course, is true, which completes the detection of cyclic loading.

For more information, please see the personal blog: Please click here.

Keywords: Java Multithreading thread

Added by badboy1245 on Tue, 08 Feb 2022 16:56:10 +0200