线程安全场景备忘:
1.加载引起的线程问题延迟加载-》使用延迟加载的单例(有的单例的实现并不需要延迟加载)-》避免线程多次实例化域,只实例化一次
2. 非延迟加载的静态域的线程安全问题:多线程修改域的属性值。注意,这个示例不存在场景1的问题。因为域是静态域且非延迟加载,并且该域私有,这样该域只会实例化一次。
public class Weibo implements java.io.Serializable {
private static final long serialVersionUID = 4282616848978535016L;
//静态域,出发点是好的,毕竟每实例化一个HttpClient太浪费资源。引出的问题:client的token非线程安全。
//解决办法:1)每个weibo的请求代码要与setToken封装起来保证原子性。这就需要很多方法使用低效的synchronized。
//2).不在客户端代码使用同步,而将HttpClient中的AccessToken定义成ThreadLocal。注意,也不是修改API源码,将HttpClient中的代码同步,这种内部同步并不是外部同步的合理替代。因为粒度不同。如果客户端调用的代码都仅限对setToken方法同步,就可以避免污染client代码,将同步放到API里面。
public static HttpClient client = new HttpClient();
public void setToken(String token) {
client.setToken(token);
}
}
3.伪线程安全问题:DaoDaoSNSWeiBoService的一些属性只需要一次性初始化并且要求保持原子性,根据需求,这是一个伪多线程的问题。不存在多个线程使用updateProperties。
public class DaoDaoSNSWeiBoService implements IDaoDaoSNSSvc {
…..
//采用非延迟加载静态域初始化。
static {
final String CLIENT_ID = "*******";
final String CLIENT_SERCRET = "******************";
final String BASEURL = "https://api.weibo.com/2/";
final String ACCESSTOKENURL = "https://api.weibo.com/2/oauth2/access_token";
final String AUTHORIZEURL = "https://api.weibo.com/2/oauth2/authorize";
//WeiboConfig只初始化一次,而Httpclient要每次都设置属性AccessToken的值。这种场景决定了Webconfig中的prop属性不应该是ThreadLocal。
//WeiboConfig没有做内部同步的必要,因为客户端代码不会细粒度地针对每个updateProperties方法的调用sync,如果客户端代码多次sync,就可以考虑内部同步。况且向外提供的updateProperties是否合理有待商量,感觉应该导出一个批量修改属性的方法。这种细粒度的方法对于外部没有意义。而且客户代码需要外部同步保证批量修改的完整性。
//由于只初始化一次,其实下面的synchronized可以去掉。
synchronized (DaoDaoSNSWeiBoService.class) {
WeiboConfig.updateProperties("client_ID", CLIENT_ID);
WeiboConfig.updateProperties("client_SERCRET", CLIENT_SERCRET);
WeiboConfig.updateProperties("baseURL", BASEURL);
WeiboConfig.updateProperties("accessTokenURL", ACCESSTOKENURL);
WeiboConfig.updateProperties("authorizeURL", AUTHORIZEURL);
}
};
…..
}