对象的共享之完全的对象构造过程

当访问共享的可变数据时候,通常都需要使用同步。。

一种是避免使用同步的方式就是不共享数据。如果仅仅在单线程内访问数据,就不需要同步。。这种技术就称为线程封闭(ThreadConfinement),它是实现线程安全性的最简单的方式之一。就是当某个对象封闭在一个线程中时,这种用法将自动实现线程安全性,即使被封闭的对象本身不是线程安全的。。

在Swing中使用了大量的线程的封闭技术。Swing的可视化组件和数据模型对象都不是线程安全的,Swing通过将他们封闭到Swing的事件分发线程中来实现线程安全性。要想正确的使用Swing,那么除了在事件线程之外的其他线程中不能访问这些对象,Swing还提供了invokeLater机制,用于将一个Runnable实例调度到事件线程中去执行),Swing应用程序的许多的并发的错误都是由于错误地在另外一个线程中使用了这些被封闭的对象。。。

线程封闭技术的另外一种常见的应用是JDBC(java Database Connectivity)的Connection对象,JDBC规范中并不要求Connection对象必须是线程安全的。在典型的服务的应用程序中,线程从连接池中获得一个Connection对象,并且用该对对象来处理请求,使用完成后将对象返还给连接池。由于大多数的请求(例如Servlet或EJB调用)都是用单个的线程采用同步的方式来处理,并且在Connection对象返还之前,连接池不再将它们分配给其他的线程,因此,这种连接管理模式在处理请求的时候隐含的将Connection对象封闭在线程中。。。

在java语言中,并没有强制的规定某个变量必须的由某个锁来保护,同样,在java语言中,也无法的强制的将对象封闭在某个线程中。线程封闭是在程序设计中的一个考虑因素,必须在程序中实现,java语言及其核心库提供了一些机制来帮助维持线程封闭性,例如局部变量和ThreadLocal类,但是即便如此,程序员依然需要负责确保封闭在线程中的对象不会从线程中溢出。。

Ad-hoc线程封闭

Ad-hoc线程封闭是指,维护线程封闭性的职责完全由程序实现来承担,,Ad-hoc线程封闭是非常脆弱的,因为没有任何一种语言特性,例如可见性修饰符或者局部变量,能将对象封闭到目标的线程上,事实上,对于线程的封闭对象(例如:GUI的应用程序中的可视化组件或者数据模型)的引用通常保存到公有的变量中。

当决定使用线程封闭技术的时候,通常是因为要将某个特定的子系统实现为一个单线程的子系统,在某些情况下,单线程子系统提供的简便性要高于Ad-hoc线程封闭技术的简便性。。

在volatile变量上存在一种特殊的的线程封闭,只要你能确保只有单个线程共享的volatile变量,执行写入的操作,那么就可以完全的在这些共享的volatile变量上执行"读取--修改--写入"的操作,。在这种情况下,相当于将修改操作封闭在单个线程中以防止发生竞态条件,并且volatile变量的可见性保证还确保了其他线程能看到最新的值。。

栈封闭

栈封闭是线程封闭的一种特殊的例子,在栈封闭中,只能通过局部变量才能访问对象。。。正如封装能使得代码变得更容易维持不变性那样,同步变量也能使对象更容易的封闭在线程中,。局部变量的固有的属性之一就是封闭在执行线程中。他们位于执行线程的栈中,其他的线程无法的访问这个栈。栈封闭(也称为线程内部使用或者线程局部使用),不要与核心库里面的ThreadLocal混淆,比Ad-hoc更容易维护,也更佳的健壮。。

对于基本类型的局部变量,例如程序清单3-9中的loadTheArk方法的numPairs。无论如何不会破坏栈封闭性。由于任何方法都无法获得对基本类型的引用,因此java语言的这种语意,确保了基本类型的局部变量始终封闭在线程内。。。

如果在线程内部使用非线程安全的对象,那么该对象依然是线程安全的。。然而更加需要小心的是,只有编写代码的开发人员,才知道哪些对象需要被封闭到执行线程中去。。以及被编写的对象是否是线程安全的。如果没有明确的说明这些需求,那么后期的维护人员很可能会使得对象逸出。。。


ThreadLocal类

维持线程封闭性的一种更加规范的的方法是使用ThreadLocal,这个类能使线程中的某个值与保存值的对象关联起来。ThreadLocal提供了get与set等访问接口的方法,这些方法为每一个使用该变量的线程都存了一份副本,因此呢,get总是返回当前执行线程在调用set时设置的最新的值。。。

Threadlocal对象通常用于防止对可变的单实例变量(SingLeton)或全局变量进行共享。。例如:在单线程应用程序中可能会维持一个全局的数据库连接,并在程序启动的时候初始化这个连接对象,从而避免了在调用每个方法时候都要传递一个Connection对象。由于JDBC的连接对象不一定是线程安全的,,因此呢,当在多线程中应用程序中没有协同的情况下使用全局变量时候,就不是线程安全的。。通常将JDBC的连接保存到ThreadLocal中,每个线程都会用于属于自己的连接。。。

程序:使用ThreadLocal来维持线程封闭性

    private static ThreadLocal<Connection> connectionHolder=new ThreadLocal<Connection>(){
        public Connection initiaValue(){
            return DriverManager.getConnection(url);
        }
    };
    private static Connection getConnnection(){
        return connectionHolder.get();

    }

当某个频繁的执行某个操作的时候需要一个临时对象,例如一个缓冲区,而同时又希望避免在每次执行的时候都应该重新的分配临时对象,就可以使用这项技术,例如:在java5之前,Integer.toString()的方法使用ThreadLocal对象来保存一个12字节的大小的缓冲区,用于对比结果进行格式化,而不是使用共享的静态缓冲区,或者每次调用的时候都分配一个新的缓冲区。。

当某个线程初次的调用ThreadLocal.get方法时候,就会调用initialValue来获取初始值。从概念上看,你可以将Thread<T>视为包含了Map<Thread,T>的对象,其中保存了特定于该线程中的值,但是ThreadLocal的实现并非如此,这些特定的线程的值,保存在Thread对象,当线程终止以后,这些值会作为垃圾回收。。。

假如你需要将一个单线程的应用程序加到一个多线程中,通常将共享的全局变量转换为ThreadLocal对象(如果全局变量的语义允许),可以维持线程安全性。。。。然而,如果将用于程序范围内的转换为线程局部的缓存,就不会又太大的作用。。

在实现应用程序框架的时候,大量的使用了ThreadLocal。例如,在EJB调用期间,,J2EE容器需要将一个事务上下文(Transaction Context)与某个执行的线程中的线程关联起来。通过事务将上下文保存在静态的ThreadLocal中,可以很容易的实现这个功能。当框架代码需要判断当前运行的是哪些事务,只需要从这个Threadlcal对象中读取事务上下文。这种机制很方便,因为它避免了在调用每个方法的时候都要传递执行上下文信息,然而这也是使用该机构和框架耦合在一起。。










评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值