30. Modo de almacenamiento local de subprocesos: sin compartir, no hay modo de diseño concurrente de daños para mejorar


Varios hilos que leen y escriben la misma variable compartida al mismo tiempo tienen problemas de concurrencia. Evite compartir para evitar problemas de concurrencia.

1. Cómo usar ThreadLocal

	static class ThreadId {
		static final AtomicLong nextId = new AtomicLong(0);
		// 定义ThreadLocal变量
		static final ThreadLocal<Long> tl = 
			ThreadLocal.withInitial(() -> nextId.getAndIncrement());

		// 此方法会为每个线程分配一个唯一的Id
		static long get() {
			return tl.get();
		}
	}
  • Llame al método get () de ThreadId dos veces antes y después de un hilo, el valor de retorno de los dos es el mismo;
  • Los dos hilos llaman al método get () de ThreadId respectivamente, luego el valor de retorno del método get () visto por los dos hilos es diferente.

SimpleDateFormat no es seguro para subprocesos. ThreadLocal se puede utilizar para resolver problemas de seguridad de subprocesos en escenarios concurrentes. Diferentes subprocesos no comparten SimpleDateFormat, al igual que las variables locales, por lo que es seguro para subprocesos.

static class SafeDateFormat {
	// 定义ThreadLocal变量
	static final ThreadLocal<DateFormat> tl = ThreadLocal
			.withInitial(() -> new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

	static DateFormat get() {
		return tl.get();
	}
}

2. Cómo funciona ThreadLocal

2.1 ThreadLocal implementado por usted mismo

Inserte la descripción de la imagen aquí

class MyThreadLocal<T> {
	Map<Thread, T> locals = new ConcurrentHashMap<>();

	// 获取线程变量
	T get() {
		return locals.get(Thread.currentThread());
	}

	// 设置线程变量
	void set(T t) {
		locals.put(Thread.currentThread(), t);
	}
}

Cree un mapa directamente, el hilo es la clave y la variable que posee el hilo es el valor.

2.2 Java implementa ThreadLocal

Inserte la descripción de la imagen aquí

class Thread {
	// 内部持有ThreadLocalMap
	ThreadLocal.ThreadLocalMap threadLocals;
}

class ThreadLocal<T> {
	public T get() {
		// 首先获取线程持有的
		// ThreadLocalMap
		ThreadLocalMap map = Thread.currentThread().threadLocals;
		// 在ThreadLocalMap中
		// 查找变量
		Entry e = map.getEntry(this);
		return e.value;
	}

	static class ThreadLocalMap {
		// 内部是数组而不是Map
		Entry[] table;

		// 根据ThreadLocal查找Entry
		Entry getEntry(ThreadLocal key) {
			// 省略查找逻辑
		}

		// Entry定义
		static class Entry extends WeakReference<ThreadLocal> {
			Object value;
		}
	}
}

También hay un mapa en la implementación de Java llamado ThreadLocalMap, pero no es ThreadLocal sino Thread el que contiene ThreadLocalMap. La clase Thread tiene un atributo privado threadLocals, cuyo tipo es ThreadLocalMap, y la clave de ThreadLocalMap es ThreadLocal.

En nuestro diseño, Map pertenece a ThreadLocal, y en la implementación de Java, ThreadLocalMap pertenece a ThreadLocal. ¿Cuál de estos dos métodos es más razonable? Obviamente, la implementación de Java es más razonable. En la implementación de Java, ThreadLocal es solo una clase de herramienta proxy. No contiene ningún dato relacionado con hilos internamente. Todos los datos relacionados con hilos se almacenan en Thread . Este diseño es fácil de entender. En términos de afinidad de datos, ThreadLocalMap pertenece a Thread es más razonable .

Hay otra razón, no es fácil causar pérdidas de memoria : en nuestro diseño, el Mapa en poder de ThreadLocal tendrá una referencia al objeto Thread, lo que significa que mientras exista el objeto ThreadLocal, el objeto Thread en el Mapa nunca Será reciclado El ciclo de vida de ThreadLocal es a menudo más largo que el hilo, por lo que este esquema de diseño es fácil de causar pérdidas de memoria. En la implementación de Java, Thread contiene ThreadLocalMap, y la referencia a ThreadLocal en ThreadLocalMap sigue siendo una referencia débil (WeakReference), por lo que siempre que el objeto Thread pueda reciclarse, ThreadLocalMap puede reciclarse. Aunque esta implementación de Java parece más complicada, es más segura.

3. ThreadLocal y pérdida de memoria

El uso de ThreadLocal en el grupo de subprocesos puede causar pérdidas de memoria.
// El autor de TODO no lo entendió muy bien, se llenó

4. InheritableThreadLocal y herencia

No recomendado

5. Resumen

El modelo de almacenamiento local de hilos es esencialmente un esquema para evitar compartir.

6. Pensar después de clase

En el trabajo real, hay muchas soluciones técnicas basadas en plataformas que usan ThreadLocal para transferir información de contexto, por ejemplo, Spring usa ThreadLocal para transferir información de transacciones. Una vez dijimos que la programación asincrónica es muy madura, entonces, ¿cree que puede usar el administrador de transacciones de Spring en un escenario asincrónico?

97 artículos originales publicados · elogiados 3 · 10,000+ vistas

Supongo que te gusta

Origin blog.csdn.net/qq_39530821/article/details/102773357
Recomendado
Clasificación