Java 并发编程之对象的共享(二)

线程封闭

一般在单线程内访问数据就不需要同步,这种技术叫做线程封闭,在Swing中大量使用了线程封闭技术,Swing的可视人化组件和数据模型对象都不是线程安全的,Swing通过将它们封闭到Swing的事件分发线程中来实现线程安全性。


线程封闭的另一个常见应用是JDBC的Connection对象,JDBC规范并不要求Connection对象必须是线程安全的。应用程序服务器连接池是线程安全的,连接池通常会由多个线程同时访问,因此非线程安全的连接池是毫无意义的。

一个安全的线程池通常要实现下列接口

    package mysql;  
    import java.sql.*;  
    import java.sql.ResultSet;  
    public interface Pool  
    {  
        public boolean start(String dbname,String user,String psw);  //启动数据库连接池服务  
        //以下start函数将允许用户设置最低空闲连接数,最高空闲连接数,最大连接数  
        public boolean start(int lows,int maxs,int maxc,String dbname,String user,String psw);  
        public Connection getConnection();  //得到连接器  
        public boolean freeConnection(Connection con);//将连接返回到连接池中  
        public boolean close(); //清除连接池,并且关闭它(使之变得不可用)  
    }  

Ad-hoc线程封闭

 这个是指,维护线程封闭性的职责完全由程序实现来承担,Ad-hoc线程封闭是非常脆弱的。因为没有语言特性可以将对象封闭到目标线程上,事实上对线程封闭对象的引用通常保存在公有变量 中。


栈封闭

栈封闭是线程封闭的一种特例。在栈封闭中只能通过局部变量才能访问对象。


	public int loadTheArk(Collection<Animal> candidates) {
		SortedSet<Animal> animals;
		int numpairs = 0;
		Animal cnadidate = nul;
		animals = new TreeSet<Animal>(new GenderComparator());
		animals.addAll = (candidates);
		for (Animal a : animals) {
			if (candidates == null || !candidates.isPotentalMate(a)) {
				cnadidate = a;
			} else {
				ark.load(new AnimalPair(candidates, a));
				++numpairs;
				candidates = null;
			}
		}
		return numpairs;
	}
例如上面程序的Numpairs对象。无论如何都不会破坏栈封闭性。由于任何方法 都 无法 获得对基本类型的引用。同时只有一个引用指向集体animals,这个引用被封闭在局部变量中,因为也被封闭在执行线程中。


ThreadLocal类

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

	public static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() {
		public Connection initialValue() {
			return DriverManager.getConnection(url);
		}
	};

	public static Connection getConnection() {
		return connectionHolder.get();
	}

这个ThreadLocal相当于 一个单线程池。当某个频繁执行的操作需要一个临时对象 ,例如 一个缓冲 区,而同时又希望避免在每次执行时都重新分配该临时对象,就可以使用这项技术 。


它会降低可重用性,也会在类之间引入隐含的耦合性,所以在使用格外小心 。

为此还做了一个小实验证明其线程安全性。

public class unsafe {
	public static ThreadLocal<Integer> connectionHolder = new ThreadLocal<Integer>() {
		
		@Override
		protected Integer initialValue() {
			// TODO Auto-generated method stub
			int a = 1;
			return a;
		}
	};

	public static void main(String[] Args) {
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				connectionHolder.set(2);
			}
		}).start();
		System.out.println(connectionHolder.get());
		connectionHolder.set(2);
		System.out.println(connectionHolder.get());
	}
}
输出结果 :

1

2

证明只可以在主线程中对其进行修改。

不变性

不可变的对象一定是线程安全的。但是不可变不等于将所有的域都声明为final类型,即使这么做了那么这个对象仍然是可变的,因为在final类型的域中可以保存对可变对象的引用 。

举个栗子

import java.util.ArrayList;

public class unsafe {
	private final static java.util.List<Integer> unsafevalue = new ArrayList<Integer>();

	public static void main(String[] agrs) {
		unsafevalue.add(2);
		System.out.println(unsafevalue.get(0));
	}
}
如果这上面个类有个get方法可以得到unsafevalue,那么仍然可以对final声明的域进行修改。


当满足三个条件时,对象才是不可变的。上面final的声明只是条件之一

  • 对象创建以后其状态不能被修改
  • 对象所有域都是Final类型
  • 对象 是正确创建的(没有this引用逸出)

再举个栗子

import java.util.ArrayList;

public final class unsafe {
	private final static java.util.List<Integer> unsafevalue = new ArrayList<Integer>();

	unsafe() {
		unsafevalue.add(2);
		unsafevalue.add(3);
		unsafevalue.add(4);
	}

	public boolean isExist(int t) {
		if (unsafevalue.contains(t)) {
			return true;
		}
		return false;
	}
}

一个好的编程习惯:

除非需要更高的可见性,否则都声明为私有域,除非需要变化,否则都声明为final类型。



安全发布的常用模式

  • 在静态初始化函数中初始化一个对象引用
  • 将对象的引用保存到volatile类型的域或者 AtomicReferance对象 中
  • 将对象的引用保存到某个正确 的构造对象中Final类型域中
  • 将对象 的引用保存到一个由锁保护的域中

java的线程安全库中提供了一下安全发布保证

  • 通过将一个键或者值放入 Hashtable,synchronizedMap或者ConcurrentMap中,可以安全的将它发发布给任何从这个引起窗口中访问它的线程
  • 通过将某个元素放入Vector,CopyOnWriteArrayList,CopyOnWriteArraySet.synchronizedList或synchronizedSet中,可以将元素安全的发布
  • 通过 将某个元素放在BLockingQueue或者ConcurrentLinkedQueue中。可以将元素安全的发布

通常要发布一个静态构造的对象 ,最简单和最安全的方式就是使用静态的初始化容器

静态初始化器由JVM在类的初始化阶段执行,由于在jvm内部存在着同步机制,因为通过 这种方式初始化的任何对象 都可以安全的发布。换句话说,就是声明为静态类型的具有最高优先级。


参考资料《java 并发编程实战》

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值