线程安全(2)--多线程逸出分析和正确处理
多线程溢出写法:
public class ThisEscape {
public ThisEscape(EventSource source) {source.registerListener(new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
});
}
}
点评:加粗的这一段隐式this事件线程已经暴露给ThisEscape构造函数,而构造函数在该类中首先被其他类调用,因此整个this都暴露了。只要其他线程在ThisEscape未构造之前(构造返回状态)调用这个类,那么this就会被新建线程共享并识别它(线程溢出)。
因此正确的写法:
public class ThisEscape{
private final EventListener listener;
private ThisEscape() {
listener = new EventListener() {
public void onEvent(Event e) {
doSomething(e);
}
};
}
public static ThisEscape newInstance(EventSource source) {
ThisEscape safe = new ThisEscape();
source.registerListener(safe.listener);
return safe;
}
点评:利用工厂模式来规避EventListener线程溢出,新建的线程无法在构造函数之前共享和识别safe,从而保证线程安全。
我们在做jdbc连接一般也是采用工厂模式,但很少考虑是否存在线程溢出的现象,应尽量避免使用静态块,因此需要引起足够的重视。
案例分析:
public class ConnectionPool {
private Vector<Connection> pool;
private String url="jdbc:mysql://localhost:3306/xxx";
private String username="root";
private String password="root";
private String driverClassName="com.mysql.jdbc.Driver";
/**
* 连接池的大小,连接数
*/
private int poolSize=10;
private static ConnectionPool instance =null;
/**
* 私有的构造方法,禁止外部创建本类的对象,要想获得本类的对,通过getIstance方法
* 使用了设计模式中的单子模式
*/
private ConnectionPool(){
init();
}
private void init(){
pool =new Vector<Connection>(poolSize);
//readConfig();
addConnection();
}
public synchronized void release(Connection conn){
pool.add(conn);
}
/**
* 关闭连接池中所有数据库的连接
*/
public synchronized void closePool(){
for(int i=0;i<pool.size();i++){
try{
((Connection)pool.get(i)).close();
}catch(SQLException e){
e.printStackTrace();
}
pool.remove(i);
}
}
/**
* 返回当前连接池中的一个对象
*/
public static ConnectionPool getInstance(){
if(instance==null){
instance=new ConnectionPool();
}
return instance;
}
/**
* 返回连接池中的一个数据库连接
*/
public synchronized Connection getConnection(){
if(pool.size()>0){
Connection conn=pool.get(0);
pool.remove(conn);
return conn;
}else{
return null;
}
}
private void addConnection(){
Connection conn=null;
for(int i=0;i<poolSize;i++){
try{
Class.forName(driverClassName);
conn=DriverManager.getConnection(url,username,password);
pool.add(conn);
}catch(ClassNotFoundException e){
e.printStackTrace();
}catch(SQLException r){
r.printStackTrace();
}
}
}
private void readConfig(){
try{
String path=System.getProperty("use.dir")+"\\dbpool.properties";
FileInputStream is=new FileInputStream(path);
Properties props=new Properties();
props.load(is);
this.driverClassName=props.getProperty("driverClassName");
this.username=props.getProperty("username");
this.password=props.getProperty("password");
this.url=props.getProperty("url");
this.poolSize=Integer.parseInt(props.getProperty("poolSize"));
}catch(Exception e){
e.printStackTrace();
System.out.println("读取属性文件错误");
}
}
点评:这个连接池算是比较健全了,但还是有不足的地方,看下标记的绿色的部分应加上final,橙色加粗部分应加上volatile