标签:loop created syn operation variant edr 同步 ret exce
答:
原文链接: https://blog.csdn.net/meism5/article/details/90413860
原文链接: https://www.baidu.com/link?url=OCQx0et9e3imugamrzeB7s6xb7naYvYcuD7HUtCfOq-o74jmZyeRM5kSBQ8Aiye_&wd=&eqid=da726f0d000041a7000000035eb8c896
在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal
their normal counterparts in that each thread that accesses one (via its
{@code get} or {@code set} method) has its own, independently initialized
copy of the variable. {@code ThreadLocal} instances are typically private
static fields in classes that wish to associate state with a thread (e.g.,
a user ID or Transaction ID)
1.1.ThreadLocal 的作用?
ThreadLocal是解决线程安全问题一个很好的思路,它通过为每个线程提供一个独立的变量副本解决了变量并发访问的冲突问题。在很多情况下,ThreadLocal比直接使用synchronized同步机制解决线程安全问题更简单,更方便,且结果程序拥有更高的并发性。
1.1.2.ThreadLocal的应用场景?
在Java的多线程编程中,为保证多个线程对共享变量的安全访问,通常会使用synchronized来保证同一时刻只有一个线程对共享变量进行操作。这种情况下可以将类变量放到ThreadLocal类型的对象中,使变量在每个线程中都有独立拷贝,不会出现一个线程读取变量时而被另一个线程修改的现象。最常见的ThreadLocal使用场景为用来解决数据库连接、Session管理等。在下面会例举几个场景。
2.Android源码中也可以看到ThreadLocal的身影
这里以andorid 源码的Handler为例子。看看Handker是怎么用ThreadLocal的。Handler就必须获取当前线程的 Looper 对象,而每一个线程的 Looper 是不一致的。因为每一个线程都会有一个 Looper 对象,因此使用 ThradLocal 去保存和获取当前线程的 Looper 就可以达到这个的效果。
2.1. Looper 内部的关于在 ThreadLocal 中存储 Looper 和 获取 Looper 的源码。
//Looper.prepare();
?
private static void prepare(boolean quitAllowed) {
if (sThreadLocal.get() != null) {
throw new RuntimeException("Only one Looper may be created per thread");
}
//将创建的 Looper 对象保存到 sThreadLocal 中。
sThreadLocal.set(new Looper(quitAllowed));
}
?
?
//从 ThreadLocal 取出 Looper 对象
public static @Nullable Looper myLooper() {
return sThreadLocal.get();
}
3.ThreadLocal的内部原理
我们从源码中了解ThreadLocal的原理,下面来看一下具体ThreadLocal是如何实现的。
ThreadLocal类中提供了几个方法:
1.public T get() { }
2.public void set(T value) { }
3.public void remove() { }
4.protected T initialValue(){ }
get()方法是用来获取ThreadLocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialValue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,下面会详细说明。
3.1.先看下get方法源码的实现
看看getMap(t)做了些什么
Get the map associated with a ThreadLocal. Overridden in
InheritableThreadLocal.
@param t the current thread
@return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
在getMap中,是调用当期线程t,返回当前线程t中的一个成员变量threadLocals。 那么我们继续取Thread类中取看一下成员变量threadLocals是什么?继续查看源码
ThreadLocalMap is a customized hash map suitable only for
maintaining thread local values. No operations are exported
outside of the ThreadLocal class. The class is package private to
allow declaration of fields in class Thread. To help deal with
very large and long-lived usages, the hash table entries use
WeakReferences for keys. However, since reference queues are not
used, stale entries are guaranteed to be removed only when
the table starts running out of space.
/
static class ThreadLocalMap {
?
/*
The entries in this hash map extend WeakReference, using
its main ref field as the key (which is always a
ThreadLocal object). Note that null keys (i.e. entry.get()
== null) mean that the key is no longer referenced, so the
entry can be expunged from table. Such entries are referred to
as "stale entries" in the code that follows.
/
static class Entry extends WeakReference<ThreadLocal<?>> {
/* The value associated with this ThreadLocal. */
Object value;
?
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
?
//省略....
}
实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类,我们继续取看ThreadLocalMap的实现。
再看setInitialValue()方法
setInitialValue()很容易理解,就是如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现。
public class ThreadLocalExsample {
ThreadLocal<Long> longLocal = new ThreadLocal<>();
public void set() {
longLocal.set(Thread.currentThread().getId());
}
public long getLong() {
return longLocal.get();
}
public static void main(String[] args) {
ThreadLocalExsample test = new ThreadLocalExsample();
//注意:没有set之前,直接get,报null异常了
System.out.println("-------threadLocal value-------" + test.getLong());
}
}
ThreadLocal的应用场景# 数据库连接
public Connection initialValue() {
return DriverManager.getConnection(DB_URL);
}
};
public static Connection getConnection() {
return connectionHolder.get();
}
ThreadLocal的应用场景# Session管理
public static Session getSession() throws InfrastructureException {
Session s = (Session) threadSession.get();
try {
if (s == null) {
s = getSessionFactory().openSession();
threadSession.set(s);
}
} catch (HibernateException ex) {
throw new InfrastructureException(ex);
}
return s;
}
ThreadLocal的应用场景# 多线程
总结
在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。 初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。 然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;
在进行get之前,必须先set,否则会报空指针异常;如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。 因为在上面的代码分析过程中,我们发现如果没有先set的话,即在map中查找不到对应的存储,则会通过调用setInitialValue方法返回i,而在setInitialValue方法中,有一个语句是T value = initialValue(), 而默认情况下,initialValue方法返回的是null。
标签:loop created syn operation variant edr 同步 ret exce
原文地址:https://www.cnblogs.com/ynzj123/p/12868249.html