[if !supportLists]1.1[endif]减少同步块范围
public boolean userHasAdminAccess(StringuserName) {
synchronized (attributesMap) {
String rights = attributesMap.get("users." + userName +".accessRights");
if (rights == null)
return false;
else
return (rights.indexOf("ADMIN") >= 0);
}
}
为创建串联的字符串“users.brian.accessRights”,编译器将创建一个临时的StringBuffer对象,调用StringBuffer.append三次,然后调用StringBuffer.toString,这意味着至少两个对象的创建和几个方法调用。接着,程序将调用HashMap.get检索该字符串,然后调用String.indexOf抽取想要的权限标识符。
public boolean userHasAdminAccess(StringuserName) {
String key = "users." + userName + ".accessRights";
String rights;
synchronized (attributesMap) {
rights = attributesMap.get(key);
}
return ((rights != null)
&& (rights.indexOf("ADMIN") >= 0));
}
需要注意StringBuffer是否为线程安全操作?
[if !supportLists]1.1[endif]减少锁粒度
public class AttributesStore {
private HashMap usersMap = new HashMap();
private HashMap servicesMap = new HashMap();
public synchronized void setUserInfo(String user, UserInfo userInfo) {
usersMap.put(user, userInfo);
}
public synchronized UserInfo getUserInfo(String user) {
return usersMap.get(user);
}
publicsynchronized void setServiceInfo(String service, ServiceInfo serviceInfo) {
servicesMap.put(service, serviceInfo);
}
public synchronized ServiceInfo getServiceInfo(String service) {
return servicesMap.get(service);
}
如果一个线程正在执行setUserInfo,不仅仅其它线程将被锁在setUserInfo和getUserInfo外面(这是我们希望的),而且这些线程也被锁在getServiceInfo和setServiceInfo外面。
public class AttributesStore {
private HashMap usersMap = new HashMap();
private HashMap servicesMap = new HashMap();
public void setUserInfo(String user, UserInfo userInfo) {
synchronized(usersMap) {
usersMap.put(user, userInfo);
}
}
public UserInfo getUserInfo(String user) {
synchronized(usersMap) {
return usersMap.get(user);
}
}
public void setServiceInfo(String service, ServiceInfo serviceInfo) {
synchronized(servicesMap) {
servicesMap.put(service, serviceInfo);
}
}
public ServiceInfo getServiceInfo(String service) {
synchronized(servicesMap) {
return servicesMap.get(service);
}
}
}
[if !supportLists]1.1[endif]信号量
如果多个线程需要访问数目很少的资源,可以使用信号量计数。将一个信号量初始化为可获得的资源数量。一旦某个线程获得了信号量,可获得的资源数减一。线程消耗完资源并释放该资源时,计数器就会加一。当信号量控制的所有资源都已被占用时,若有线程试图访问此信号量,则会进入阻塞状态,直到有可用资源被释放。
class Semaphore {
private int count;
public Semaphore(int n) {
this.count = n;
}
public synchronized void acquire() {
while(count == 0) {
try {
wait();
} catch (InterruptedException e) {
//keep trying
}
}
count--;
}
public synchronized void release() {
count++;
notify(); //alert a thread that's blocking on this semaphore
}
}
[if !supportLists]1.1[endif]什么是可见性
在一个单线程程序中,如果首先改变一个变量的值,再读取该变量的值的时候,所读取到的值就是上次写操作写入的值。也就是说前面操作的结果对后面的操作是肯定可见的。但是在多线程程序中,如果不使用一定的同步机制,就不能保证一个线程所写入的值对另外一个线程是可见的。造成这种情况的原因可能有下面几个:
[if !supportLists]l[endif]CPU内部的缓存:现在的CPU一般都拥有层次结构的几级缓存。CPU直接操作的是缓存中的数据,并在需要的时候把缓存中的数据与主存进行同步。因此在某些时刻,缓存中的数据与主存内的数据可能是不一致的。某个线程所执行的写入操作的新值可能当前还保存在CPU的缓存中,还没有被写回到主存中。这个时候,另外一个线程的读取操作读取的就还是主存中的旧值。
[if !supportLists]l[endif]CPU的指令执行顺序:在某些时候,CPU可能改变指令的执行顺序。这有可能导致一个线程过早的看到另外一个线程的写入操作完成之后的新值。
[if !supportLists]l[endif]编译器代码重排:出于性能优化的目的,编译器可能在编译的时候对生成的目标代码进行重新排列。