线程里可以使用定义在主内存里的变量。
也能通过ThreadLocal存储线程本地变量。(每一个线程有一个自己的变量,所以不存在多个线程同时对主内存中的一个变量进行访问)
用多个线程爬取不同网站,每个线程用自身的ThreadLocal来维护爬取不成功的列表。
使用ThreadLocal:
class MyThreadLocal{
//定义一个ThreadLocal变量,用来保存当前线程私有数据
private ThreadLocal<Integer> localVal = new ThreadLocal<Integer>(){
protected Integer initialValue(){
return 0;
}
};
private ThreadLocal<String> curName = new ThreadLocal<String>();
public Integer add(){
//将值+1,并更新
localVal.set(localVal.get() + 1);
return localVal.get() + 1;
}
public String setCurName(String threadName){
try {
curName.set(threadName);
return curName.get();
}finally {
curName.remove();
}
}
}
class UserLocalvALThread extends Thread{
private MyThreadLocal localObj = new MyThreadLocal();
public UserLocalvALThread(MyThreadLocal localObj){
this.localObj = localObj;
}
public void run(){
for (int i = 0; i < 3; ++i){
System.out.println(localObj.add() + "\t" + localObj.setCurName(Thread.currentThread().getName()));
}
}
}
public class ThreadLocalDemo {
public static void main(String[] args){
MyThreadLocal threadLocal = new MyThreadLocal();
Thread t1 = new UserLocalvALThread(threadLocal);
Thread t2 = new UserLocalvALThread(threadLocal);
Thread t3 = new UserLocalvALThread(threadLocal);
t1.start();
t2.start();
t3.start();
}
}
输出:
2 Thread-0
2 Thread-2
2 Thread-1
3 Thread-2
3 Thread-0
4 Thread-2
3 Thread-1
4 Thread-0
4 Thread-1
不用ThreadLocal:
class MyThreadLocal{
private int test =0;
private String testString;
public Integer add(){
//将值+1,并更新
test++;
return test;
}
public String setCurName(String threadName){
testString = threadName;
return testString;
}
}
class UserLocalvALThread extends Thread{
private MyThreadLocal localObj = new MyThreadLocal();
public UserLocalvALThread(MyThreadLocal localObj){
this.localObj = localObj;
}
public void run(){
for (int i = 0; i < 3; ++i){
System.out.println(localObj.add() + "\t" + localObj.setCurName(Thread.currentThread().getName()));
}
}
}
public class ThreadLocalDemo {
public static void main(String[] args){
MyThreadLocal threadLocal = new MyThreadLocal();
Thread t1 = new UserLocalvALThread(threadLocal);
Thread t2 = new UserLocalvALThread(threadLocal);
Thread t3 = new UserLocalvALThread(threadLocal);
t1.start();
t2.start();
t3.start();
}
}
输出结果:
1 Thread-0
4 Thread-0
3 Thread-2
6 Thread-2
7 Thread-2
2 Thread-1
8 Thread-1
5 Thread-0
9 Thread-1
初始化均使用的是同一个threadLocal,使用ThreadLocal和不使用结果如上。
set方法:调用时传入一个参数,放入ThreadLocalMap对象,用当前ThreadLocal为Key,如果没有ThreadLocalMap对象,由当前线程t创建。
get方法:通过当前线程,得到所属的ThreadLocalMap,以及Entry,从Entry中获得Value。
ThreadLocalMap:通过内部的ThreadLocalMap对象来存储本地变量。
ThreadLocalMap里每个元素是Entry。
每个Entry是键值对,键是当前ThreadLocal对象,值是本地变量。
Entry是继承WeakReference,是弱引用。
一个线程类可以有多个ThreadLocal变量。(上面的就两个)
一个线程类里有一个ThreadLocalMap。
一个ThreadLocalMap里有多个Entry。
每个Entry存储了一个ThreadLocal本地变量。
在ThreadLocal里保存值时,会放入Entry对象。Entry是基于弱引用,如创建ThreadLocal的线程终止,会回收。但是如果创建ThreadLocal的线程一直运行,value就可能无法回收。所以及时remove。
说法总结:
1.在xx业务用到ThreadLocal保存本地变量。
2.指出内部实现是ThreadLocalMap。说明具体关系。
3.结合set,get等源码讲述赋值和取值。
4.结合Entry弱引用,讲述可能引发内存泄漏,及时remove。
5.引导到垃圾回收话题