51,394
社区成员




public class MyServlet extends Servlet{
private static Map<String, String> fileName2Data = new HashMap<String, String>();
private void processFile3(String fName){
String data = fileName2Data.get(fName);
if(data==null){
data = readFromFile(fName); //耗时28ms
fileName2Data.put(fName, data);
}
//process with data
}
}
看起来好像还不错,建立一个文件名和文件数据的映射。如果读取一个map中已经存在的数据,那么就不不用读取文件了。
可是问题在于,Servlet是并发,上面会导致一个很严重的问题,死循环。因为,HashMap在并发修改的时候,可能是导致循环链表的构成!!!(具体你可以自行阅读HashMap源码)如果你没接触过多线程,可能到时候发现服务器没请求也巨卡,也不知道什么情况!
好的,那就用ConcurrentHashMap,正如他的名字一样,他是一个线程安全的HashMap,这样能轻松解决问题。
public class MyServlet extends Servlet{
private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();
private void processFile3(String fName){
String data = fileName2Data.get(fName);
if(data==null){
data = readFromFile(fName); //耗时28ms
fileName2Data.put(fName, data);
}
//process with data
}
}
这样真的解决问题了吗,这样虽然只要有用户访问过文件a,那另一个用户想访问文件a,也会从fileName2Data中拿数据,然后也不会引起死循环。
可是,如果你觉得这样就已经完了,那你把多线程也想的太简单了,骚年!
你会发现,1000个用户首次访问同一个文件的时候,居然读取了1000次文件(这是最极端的,可能只有几百)。What the fuckin hell!!!
难道代码错了吗,难道我就这样过我的一生!
好好分析下。Servlet是多线程的,那么
public class MyServlet extends Servlet{
private static ConcurrentHashMap<String, String> fileName2Data = new ConcurrentHashMap<String, String>();
private void processFile3(String fName){
String data = fileName2Data.get(fName);
//“偶然”-- 1000个线程同时到这里,同时发现data为null
if(data==null){
data = readFromFile(fName); //耗时28ms
fileName2Data.put(fName, data);
}
//process with data
}
}
上面注释的“偶然”,这是完全有可能的,因此,这样做还是有问题。
因此,可以自己简单的封装一个任务来处理。
public class MyServlet extends Servlet{
private static ConcurrentHashMap<String, FutureTask> fileName2Data = new ConcurrentHashMap<String, FutureTask>();
private static ExecutorService exec = Executors.newCacheThreadPool();
private void processFile3(String fName){
FutureTask data = fileName2Data.get(fName);
//“偶然”-- 1000个线程同时到这里,同时发现data为null
if(data==null){
data = newFutureTask(fName);
FutureTask old = fileName2Data.putIfAbsent(fName, data);
if(old==null){
data = old;
}else{
exec.execute(data);
}
}
String d = data.get();
//process with data
}
private FutureTask newFutureTask(final String file){
return new FutureTask(new Callable<String>(){
public String call(){
return readFromFile(file);
}
private String readFromFile(String file){return "";}
}
}
}
以上所有代码都是直接在bbs打出来的,不保证可以直接运行。