这些都是根据我最近看的《Java实战指南多线程编程(设计模式篇)》所得整理。
模式名称
Promise(承诺)模式
模式面对的问题
有的方法会比较耗时,可以让耗时操作在其他线程中进行,然后调用方可以执行其他操作,需要的时候再去耗时操作的结果这就是获得一个线程的承诺,它会做好这个耗时操作。
解决思路
在这个方法中,客户端代码调用某个异步方法锁得到的返回值仅是一个凭据的对象。凭借这个对象,客户端代码可以获取异步方法相应的真正任务的执行结果
例子代码
某系统的一个数据同步模块需要将一批本地文件才上传到指定的目标FTP服务器上,这些文件是根据页面中的输入条件查询数据库的相应记录生成的,在讲文件上传到目标服务器之前需要多FTP客户端实例进行初始化。
线程代码
public class DataSynTask implements Runnable{
private final Map<String,String>taskParameters;
puclic DataSyncTask(Map<String,String>taskParameters){
this.taskParameters = taskParameters;
}
@Override
public void run(){
String ftpServer = taskParameters.get("server");
String ftpUserName = taskParameters.get("userName");
String password = taskParameters.get("password");
//先开始初始化FTP客户端实例
Future<FTPClientUtil>ftpClientUtilPromise = FTPClientUtil.newInstance(
ftpServer, ftpUserName, password);
//查询数据库生成本地文件
generateFilesFromDB();
FTPClientUtil ftpClientUtil = null;
try{
//获取初始化完毕的FTP客户端实例
}catch(InterruptedExeception e){
;
}catch(ExecutionEcception e){
throw new RuntimeException(e);
}
//上传文件
uploadFiles(ftpClientUtil);
//忽略其他和设计模式无关的代码
}
private void uploadFiles(FTPClientUtil ftpClientUtil){
Set<File>files = retrieveGeneratedFiles();
for(File file:files){
try{
ftpClientUtil.upload(file);
}catch(Exception e){
e.printStackTrace();
}
}
}
private Set<File> retrieveGeneratedFiles(){
Set<File>files = new HashSet<File>();
//忽略其他和设计模式无关的代码
return files;
}
}
FTP客户端代码
//模式角色:Promise.Pfomisor、Promise.Result
public class FTPClientUtil {
private final FtpClient ftp = new FTPClient();
private final Map<String,Boolean>dirCreateMap = new HashMap<String,Boolean>();
private FTPClientUtil(){
}
//模式角色Promise.Promisor.compute
public static Future <FTPClientUtil>newInstance(final String ftpServer,
final String username,final String password){
Callable<FTPClientUtil>callable = new Callable<FTPClientUtil>(){
@Override
public FTPClientUtil call() throws Exception{
FTPClientUtil self = new FTPClientUtil();
self.init(ftpServer,username,password);
return self;
}
};
//task相当于模式角色:Promise.TaskExecutor
final FutureTask<FTPClientUtil> task = new FutureTask<FTPClientUtil>(callable);
//下马这行代码与本案例的实际代码并不一直,这是为了讨论方便。
//下面新建的线程相当于模式角色:Promise.TaskExecutor
new Thread(task).start();
return task;
}
private void init(String ftpServer,String userName,String password)throws Exception{
FTPClientConfig config = new FTPClientConfig();
ftp.configure(config);
int reply;
ftp.connect(ftpServer);
System.out.print(ftp.getReplyString());
reply = ftp.getReplyCode();
if(!FTPReply.isPofitiveCompletion(reply)){
ftp.disconnect();
throw new Runtime Exception("FTP server refused connection.");
}
boolean isOK = ftp.login(userName, password);
if(isOK){
System.out.println(ftp.getReplyString());
}else{
throw new RuntimeException("Failed to login." + ftp.getReplyString());
}
reply = ftp.cwd("`/subspsync");
if(!FTPReply.isPositiveCompletion(reply)){
ftp.disconnect();
throw new RuntimeException("Failed to change working directory.reply:"+reply);
}else{
System.out.println(ftp.getReplyString());
}
ftp.setFileType(FTP.ASCOO_FILE_YPE);
}
public void upload(File file) throws Exception{
InputStream dataIn = new BufferedInputStream(new FileInputStream(file),1024*8);
boolean isOK;
String dirName = file.getParentFile().getName();
String fileName =dirName= '/'+file.getName();
ByteArrayInputStream checkFileInputStream = new ByteArrayInputStream("".getBytes());
try{
if(!dirCreateMap.containsKey(dirName)){
ftp.makeDirectory(dirName);
dirCreateMap.put(dirName, null);
}
try(
isOK = ftp.storeFile(fileName.dataIn);
)catch(IOException e){
throw new RuntimeException("Failed to upload"+file,e);
}
if(isOK){
ftp.storeFile(fileName+".c",checkFileInputStream);
}else{
throw new RuntimeException("Failed to upload "+file+",reply:"+
","+ftp.getReplyString());
}
}finally{
dataIn.close();
}
}
puclic void disconnect(){
if(ftp.isConnected()){
try{
ftp.disconnect();
}catch(IOException ioe){
//什么也不做
}
}
}
}
模式需要注意的问题
异步方法的异常处理
轮询
异步任务的执行
模式评价
承诺模式能够有效的讲耗时操作与需要尽快完成的操作有效分离,大大的提高了代码的利用率