ThreadLocal 使用实例
使用ThreadLocal创建的对象只能被当前线程访问,每个线程保存一个对象的副本,在多线程操作时是线程安全的。然后通过重写initialValue()方法,可以给初始值。每次调用get方法,先检查当前线程是否有这个THreadLocal对象,如果没有调用initialValue方法,或者返回null
上代码:
public class ThreadLocalTest {
public static void main(String[] args) throws InterruptedException {
final ThreadLocalTest test = new ThreadLocalTest();
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
// 在这里新建了一个线程
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
test.set(); //
System.out.println("Thread1==yield");
try{
Thread.currentThread().yield();
}catch (Exception e){}
System.out.println(Thread.currentThread().getName()+"==="+test.getLong());
System.out.println(Thread.currentThread().getName()+"==="+test.getString());
}
},"Thread1") ;
thread1.start();
System.out.println(test.getLong());
System.out.println(test.getString());
}
ThreadLocal longLocal = new ThreadLocal();
ThreadLocal stringLocal = new ThreadLocal(){
@Override
protected String initialValue() {
return "";
}
};
public void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
public long getLong() {
return longLocal.get();
}
public String getString() {
return stringLocal.get();
}
}
输出:
1
main
1
main
Thread1==yield
Thread1===11
Thread1===Thread1
我们可以发现每个ThreadLocal变量,被放在线程的一个map中,然后get的时候,key就是ThreadLocal变量本身。也就是应该是这么个map
。
InheritableThreadLocal 使用实例
首先InheritableThreadLocal,继承自ThreadLocal. 但是这个是父线程创建的THreadLocal可以在子线程中使用,也可以在线程本身中使用。
上代码:
public class InheritableThreadLocalTest {
public static InheritableThreadLocal inheritableThreadLocal =
new InheritableThreadLocal();
public static ExecutorService executorService = Executors.newFixedThreadPool(1);
public static void main(String[] args) {
inheritableThreadLocal.set("xxxxx");
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"==get "+
inheritableThreadLocal.get()+" from main Thread" );
}
});
try{
Thread.sleep(1000);
}catch (Exception e){}
inheritableThreadLocal.set("xxx2");
executorService.submit(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"==get "+
inheritableThreadLocal.get()+" from main Thread" );
}
});
System.out.println(Thread.currentThread().getName()+"get==="+inheritableThreadLocal.get());
try{
Thread.sleep(2000);
}catch (Exception e){}
executorService.shutdown();
}
}
结果输出:
pool-1-thread-1==get xxxxx from main Thread
pool-1-thread-1==get xxxxx from main Thread
mainget===xxx2
我们可以发现,线程池中第二次提交的任务,还是获取的xxxxx ,并没有获取到xxx2 ,因为线程池中只有一个线程,并没有销毁掉,所以也就不会去初始化 thread1 get的时候,get的是自己的threadLocals变量
ThreadLocal源码分析
先看类的结构,类内部,有一个静态内部类,ThreadLocalMap,就是Thread类的threadLocals 引用的这个map对象.
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
static class ThreadLocalMap {
static class Entry extends WeakReference> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
private int threshold; // Default to 0
private Entry getEntry(ThreadLocal> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
private Entry getEntryAfterMiss(ThreadLocal> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
}
ThreadLocal核心主要是上述代码。首先set方法,先拿到当前线程的ThreadLocalMap,然后如果map!=null,就set进去。复杂的是get方法,在getEntry()时,如果没有命中的话,会触发expungeStaleEntry方法,回收key为null的Entry对象.由于Entry对象和ThreadLocal对象extends 软引用,当对象只有Softrefrence的时候,在内存不足的时候,会回收内存。
4种引用
Refrence 的子类
4种引用
我们都知道在Java中有4种引用,这四种引用从高到低分别为:
- StrongReference
这个引用在Java中没有相应的类与之对应,但是强引用比较普遍,例如:Object obj = new Object();这里的obj就是要给强引用,如果一个对象具有强引用,则垃圾回收器始终不会回收此对象。当内存不足时,JVM情愿抛出OOM异常使程序异常终止也不会靠回收强引用的对象来解决内存不足的问题。
- SoftReference
如果一个对象只有软引用,则在内存充足的情况下是不会回收此对象的,但是,在内部不足即将要抛出OOM异常时就会回收此对象来解决内存不足的问题。
- WeakReference
WeakReference 基本与SoftReference 类似,只是回收的策略不同。
只要 GC 发现一个对象只有弱引用,则就会回收此弱引用对象。但是由于GC所在的线程优先级比较低,不会立即发现所有弱引用对象并进行回收。只要GC对它所管辖的内存区域进行扫描时发现了弱引用对象就进行回收。 - PhantomReference
Reference
主要是负责内存的一个状态,当然它还和java虚拟机,垃圾回收器打交道。Reference类首先把内存分为4种状态Active,Pending,Enqueued,Inactive。
- Active,一般来说内存一开始被分配的状态都是 Active,
- Pending 大概是指快要被放进队列的对象,也就是马上要回收的对象,
- Enqueued 就是对象的内存已经被回收了,我们已经把这个对象放入到一个队列中,方便以后我们查询某个对象是否被回收,
- Inactive就是最终的状态,不能再变为其它状态。
ReferenceQueue
引用队列,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到队列中,ReferenceQueue实现了入队(enqueue)和出队(poll),还有remove操作,内部元素head就是泛型的Reference。