b2科目四模拟试题多少题驾考考爆了怎么补救
b2科目四模拟试题多少题 驾考考爆了怎么补救

Java并发编程:深入剖析ThreadLocal(2)

电脑杂谈  发布时间:2019-05-22 22:22:37  来源:网络整理

那么我们继续取Thread类中取看一下成员变量threadLocals是什么:

实际上就是一个ThreadLocalMap,这个类型是ThreadLocal类的一个内部类,我们继续取看ThreadLocalMap的实现:

threadlocal应用场景_websocket应用场景_嘟嘟美甲 应用场景

但是使用类的方式来实现厂模式还有更多的好处. 其一是: 我们可以管理从 nsisupports 接口派生而来的类厂本身的生存期. 当我们试图把多个类厂划分成一组, 然后确定是否能卸载这一组类厂的时候, 这一点非常重要. 另一个好处是: 类厂可以引入其他需要支持的接口. 在我们后面讨论 nsiclassinfo 接口的时候, 我们会看到某些类厂使用这个接口支持信息查询, 诸如这个对象是用什么语言写的, 对象支持的接口等等. 这种派生自 nsisupports 的 "future-proofing" 特性非常关键.xpidl 与类型库定义接口的简单而强劲的方法是使用接口定义语言 - 这实际上是在一个跨平台而语言无关开发环境下定义接口的需求. xpcom 使用的是源自于 corba omg 接口定义语言的变体, 称为 xpidl, 来定义接口, xpidl 可以定义接口的方法, 属性, 常量, 以及接口继承.采用 xpidl 定义接口还存在一些缺陷. 它不支持多继承, 同时 xpidl 定义的方法名不能相同。所以,为了安全地使用threadlocal,必须要像每次使用完锁就解锁一样,在每次使用完threadlocal后都要调用remove()来清理无用的entry。它的key为threadlocal对象而且还使用了weakreference,threadlocalmap正是用来存储变量副本的。

然后再继续看setInitialValue方法的具体实现:

很容易了解,就是如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现:

至此,可能大部分朋友已经明白了ThreadLocal是如何为每个线程创建变量的副本的:

get()方法是用来获取threadlocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialvalue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,下面会详细说明。session的get根据当前线程返回其对应的线程内部变量,也就是我们需要的net.sf.hibernate.session(相当于对应每个连接).多线程情况下共享链接是不安全的。注意prepare(boolean)方法中,有一个sthreadlocal变量,这个变量有点像一个哈希表,它的key是当前的线程,也就是说,它可以存储一些数据/引用,这些数据/引用是与当前线程是一一对应的,在这里的作用是,它判断一下当前线程是否有looper这个对象,如果有,那么就报错了,"only one looper may be created per thread",一个线程只允许创建一个looper,如果没有,就new一个新的塞进这个哈希表中。

get()方法是用来获取threadlocal在当前线程中保存的变量副本,set()用来设置当前线程中变量的副本,remove()用来移除当前线程中变量的副本,initialvalue()是一个protected方法,一般是用来在使用时进行重写的,它是一个延迟加载方法,下面会详细说明。上述代码定义了一个名为name的变量,该变量可以用来保存任何值(像这样未经过初始化的变量,会保存一个特殊的值--undefined),也可以初始化变量的值,。第一种形式: 定义一个类,它的构造函数为private的,它有一个static的private的该类变量,在类初始化时实例话,通过一个public的getinstance方法获取对它的引用,继而调用其中的方法。

然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。

下面通过一个例子来证明通过ThreadLocal能达到在每个线程中创建变量副本的效果:

public class Test {
 ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
 ThreadLocal<String> stringLocal = new ThreadLocal<String>();
 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();
 }
 public static void main(String[] args) throws InterruptedException {
 final Test test = new Test();
 test.set();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 Thread thread1 = new Thread(){
 public void run() {
 test.set();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 };
 };
 thread1.start();
 thread1.join();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 }
}

这段代码的输出结果为:

threadlocal应用场景_嘟嘟美甲 应用场景_websocket应用场景

请看下面的例子(参考),下面的例子中,主线程等待 th 线程输入一个值,然后将 th 线程从终端接收的值打印出来,在 th 线程接受到值之前,主线程一直等待,每个一秒超时一次,并打印一个 ".":。下例首先生成了两个线程实例t1和t2,然后分别设置它们的优先级,接着启动两线程(两线程基本一样,只不过它们输出不一样,t1为“1”,t2为“2”,根据它们各自输出字符个数比可大致看出它们占用cpu时间之比,这也反映出了它们各自的优先级)。从打印结果看以看出,第一次打印的时候成功的改变了point的值当我们第二次打印得到的值却是0。

总结一下:

1)实际的通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;

2)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;

3)在进行get之前,必须先set,否则会报空指针异常;

如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。

大家注意图中红色标注的地址出现了8次:0x1102db280,这个是在调用class_getmethodimplementation()方法时,无法找到对应实现时返回的相同的一个地址,无论该方法是在实例方法或类方法,无论是否对一个实例调用该方法,返回的地址都是相同的,但是每次运行该程序时返回的地址并不相同,而对于另一种方法,如果找不到对应的实现,则返回0,在图中我做了蓝色标记。el表达式语句在执行时,会调用pagecontext.findattribute方法,用标识符为关键字,分别从page、request、session、application四个域中查找相应的对象,找到则返回相应的对象,找不到则返回“”(不是null)。(2)存储函数可以通过return语句将运算的结果返回,但是存储过程不允许调用return语句,存储过程可以通过调用out参数,将运算的结果返回给外界。

看下面这个例子:

public class Test {
 ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
 ThreadLocal<String> stringLocal = new ThreadLocal<String>();
 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();
 }
 public static void main(String[] args) throws InterruptedException {
 final Test test = new Test();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 Thread thread1 = new Thread(){
 public void run() {
 test.set();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 };
 };
 thread1.start();
 thread1.join();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 }
}

在main线程中,没有先set,直接get的话,运行时会报空指针异常。

但是如果改成下面这段代码,即重写了initialValue方法:

public class Test {
 ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){
 protected Long initialValue() {
 return Thread.currentThread().getId();
 };
 };
 ThreadLocal<String> stringLocal = new ThreadLocal<String>(){;
 protected String initialValue() {
 return Thread.currentThread().getName();
 };
 };
 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();
 }
 public static void main(String[] args) throws InterruptedException {
 final Test test = new Test();
 test.set();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 Thread thread1 = new Thread(){
 public void run() {
 test.set();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 };
 };
 thread1.start();
 thread1.join();
 System.out.println(test.getLong());
 System.out.println(test.getString());
 }
}

就可以直接不用先set而直接调用get了。

三.ThreadLocal的应用场景

最常见的ThreadLocal使用场景为 用来解决 连接、Session管理等。

如:


本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-100630-2.html

相关阅读
    发表评论  请自觉遵守互联网相关的政策法规,严禁发布、暴力、反动的言论

    热点图片
    拼命载入中...