package com.fei;
/**
* 每个线程代表一所学校,学校下面有年级
*
*/
public class DataShare {
public static void main(String[] args) throws InterruptedException {
Thread qinghua=new Thread(new Runnable(){
@Override
public void run() {
School.getSchool().setName("清华大学");//修改学校名称
new GradeOne().print();
new GradeTwo().print();
}
});
qinghua.setName("qinghua");
qinghua.start();
qinghua.join();
Thread beijing =new Thread(new Runnable(){
@Override
public void run() {
//该学校发现School类中的学校名称和该校一样,故使用默认学校
new GradeOne().print();
new GradeTwo().print();
}
});
beijing.setName("beijing");
beijing.start();
}
}
class School{
private String name="北京大学";
private static School instance=null;
private School(){};
public static School getSchool(){
if(instance==null){
synchronized(School.class){
if(instance==null)
instance=new School();
}
}
return instance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class GradeOne{
public void print(){
System.out.println(Thread.currentThread().getName()+" :"+School.getSchool().getName()+"一年级");
}
}
class GradeTwo{
public void print(){
System.out.println(Thread.currentThread().getName()+" :"+School.getSchool().getName()+"二年级");
}
} 运行结果 qinghua :清华大学一年级 qinghua :清华大学二年级 beijing :清华大学一年级 beijing :清华大学二年级 代码很简单,结果意料之中,因为清华大学和北京大学都使用了同一个School对象,所以清华大学修改学校名称时,北京大学也受到了影响。 现在的需求时,清华大学修改学校名称时,北京大学不受影响。也就是说每个学校都拥有自己的School对象,其他学校不能干扰,但是同一所学校内的所有年级使用School对象时却又是同一个的。那么我们可以把程序这样来改。 package com.fei;
import java.util.HashMap;
import java.util.Map;
/**
* 每个线程代表一所学校,学校下面有年级
*
*/
public class DataShare {
public static void main(String[] args) throws InterruptedException {
Thread qinghua=new Thread(new Runnable(){
@Override
public void run() {
School.getSchool().setName("清华大学");//修改学校名称
new GradeOne().print();
new GradeTwo().print();
}
});
qinghua.setName("qinghua");
qinghua.start();
qinghua.join();
Thread beijing =new Thread(new Runnable(){
@Override
public void run() {
//该学校发现School类中的学校名称和该校一样,故使用默认学校
new GradeOne().print();
new GradeTwo().print();
}
});
beijing.setName("beijing");
beijing.start();
}
}
class School{
private String name="北京大学";
private static School instance=null;
//把每个线程对应的学校放到map中来
private static Map<Thread,School> map=new HashMap<Thread,School>();
private School(){};
/**
* 不需要使用synchronized了,因为就算多个线程同时进来,也是各取个的学校
*/
public static School getSchool(){
instance = map.get(Thread.currentThread());
if (instance == null) {
instance = new School();
map.put(Thread.currentThread(), instance);
}
return instance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
class GradeOne{
public void print(){
System.out.println(Thread.currentThread().getName()+" :"+School.getSchool().getName()+"一年级");
}
}
class GradeTwo{
public void print(){
System.out.println(Thread.currentThread().getName()+" :"+School.getSchool().getName()+"二年级");
}
} 运行结果: qinghua :清华大学一年级 qinghua :清华大学二年级 beijing :北京大学一年级 beijing :北京大学二年级 我们实现需求了。 为了解决此类需求,在JDK1.5中,为我们提供了ThreadLocal这个类,我们可以将上面程序的School类做如下修改即可。为了节约篇幅,就只贴School类的源码了。 class School{
private String name="北京大学";
private static School instance=null;
//把每个线程对应的学校放到map中来
// private static Map<Thread,School> map=new HashMap<Thread,School>();
private static ThreadLocal<School> threadLocal=new ThreadLocal<School>();
private School(){};
/**
* 不需要使用synchronized了,因为就算多个线程同时进来,也是各取个的学校
*/
public static School getSchool(){
// instance = map.get(Thread.currentThread());
instance=threadLocal.get();
if (instance == null) {
instance = new School();
// map.put(Thread.currentThread(), instance);
threadLocal.set(instance);
}
return instance;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
} 上面的程序中我们用到了ThreadLocal类中的 set(T value)和get()方法,那么我们来看看他们的源码. public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//根据当前线程对象去取得当前线程中的ThreadLocalMap对象,是线程独自拥有的,不和其他线程公用
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue();
} 我们暂不去过分追究它的详细实现,我们从这个源码段中了解大意。表面的大意太简单明了了,也就不多说了,接下来,看看setInitialValue() private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
} 对于这方法,我们去看看initialValue()即可,其他的大意都很简单。 protected T initialValue() {
return null;
} 该方法返回null,也就是说初始值是null。 所以将上面的几个方法连起来看,就是get()如果ThreadLoal中还没T对象,则返回null。 接下来看看set(T value) public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
} 这代码在上面出现过了,大意也很明了。 |