抓取网页生成电子书(继续并发专题~FutureTask有点类似Runnable的get方法支持阻塞)
优采云 发布时间: 2022-03-25 03:17抓取网页生成电子书(继续并发专题~FutureTask有点类似Runnable的get方法支持阻塞)
继续并发话题~
FutureTask 有点类似于 Runnable,可以通过 Thread 启动,但是 FutureTask 可以返回执行的数据,FutureTask 的 get 方法支持阻塞。
因为:FutureTask可以返回执行的数据,而FutureTask的get方法支持阻塞这两个特性,我们可以用它来预加载一些可能会用到的资源,然后在用到的时候调用get方法来获取(如果加载了资源,则直接返回;否则继续等待其加载完成)。
这里有两个例子:
1、使用 FutureTask 预加载数据以备后用。
package com.zhy.concurrency.futuretask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 使用FutureTask来提前加载稍后要用到的数据
*
* @author zhy
*
*/
public class PreLoaderUseFutureTask
{
/**
* 创建一个FutureTask用来加载资源
*/
private final FutureTask futureTask = new FutureTask(
new Callable()
{
@Override
public String call() throws Exception
{
Thread.sleep(3000);
return "加载资源需要3秒";
}
});
public final Thread thread = new Thread(futureTask);
public void start()
{
thread.start();
}
/**
* 获取资源
*
* @return
* @throws ExecutionException
* @throws InterruptedException
*/
public String getRes() throws InterruptedException, ExecutionException
{
return futureTask.get();//加载完毕直接返回,否则等待加载完毕
}
public static void main(String[] args) throws InterruptedException, ExecutionException
{
PreLoaderUseFutureTask task = new PreLoaderUseFutureTask();
/**
* 开启预加载资源
*/
task.start();
// 用户在真正需要加载资源前进行了其他操作了2秒
Thread.sleep(2000);
/**
* 获取资源
*/
System.out.println(System.currentTimeMillis() + ":开始加载资源");
String res = task.getRes();
System.out.println(res);
System.out.println(System.currentTimeMillis() + ":加载资源结束");
}
}
运行结果:
1400902789275:开始加载资源
加载资源需要3秒
1400902790275:加载资源结束
可以看到,原本加载资源需要 3 秒,现在只需 1 秒。如果用户有其他操作时间较长的,可以直接返回,大大提升了用户体验。
2、 看看 Future 的 API
可以看到Future的API比较简单。可以看名字,get(long,TimeUnit)也可以支持,设置最长等待时间。例如,如果某个操作耗时过长,您可以取消它。
3、FutureTask 模拟,预载功能供用户在线观看电子书
用户观看当前页面时,后台预加载下一页,可以大大提升用户体验,无需等待每一页加载完毕,用户会觉得这个电子书软件很流畅,哈哈,用户认为很好,只有真的很好。
package com.zhy.concurrency.futuretask;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 使用FutureTask模拟预加载下一页图书的内容
*
* @author zhy
*
*/
public class BookInstance
{
/**
* 当前的页码
*/
private volatile int currentPage = 1;
/**
* 异步的任务获取当前页的内容
*/
FutureTask futureTask = new FutureTask(
new Callable()
{
@Override
public String call() throws Exception
{
return loadDataFromNet();
}
});
/**
* 实例化一本书,并传入当前读到的页码
*
* @param currentPage
*/
public BookInstance(int currentPage)
{
this.currentPage = currentPage;
/**
* 直接启动线程获取当前页码内容
*/
Thread thread = new Thread(futureTask);
thread.start();
}
/**
* 获取当前页的内容
*
* @return
* @throws InterruptedException
* @throws ExecutionException
*/
public String getCurrentPageContent() throws InterruptedException,
ExecutionException
{
String con = futureTask.get();
this.currentPage = currentPage + 1;
Thread thread = new Thread(futureTask = new FutureTask(
new Callable()
{
@Override
public String call() throws Exception
{
return loadDataFromNet();
}
}));
thread.start();
return con;
}
/**
* 根据页码从网络抓取数据
*
* @return
* @throws InterruptedException
*/
private String loadDataFromNet() throws InterruptedException
{
Thread.sleep(1000);
return "Page " + this.currentPage + " : the content ....";
}
public static void main(String[] args) throws InterruptedException,
ExecutionException
{
BookInstance instance = new BookInstance(1);
for (int i = 0; i < 10; i++)
{
long start = System.currentTimeMillis();
String content = instance.getCurrentPageContent();
System.out.println("[1秒阅读时间]read:" + content);
Thread.sleep(1000);
System.out.println(System.currentTimeMillis() - start);
}
}
}
输出结果:
[1秒阅读时间]read:Page 1 : the content ....
2001
[1秒阅读时间]read:Page 2 : the content ....
1000
[1秒阅读时间]read:Page 3 : the content ....
1001
[1秒阅读时间]read:Page 4 : the content ....
1000
[1秒阅读时间]read:Page 5 : the content ....
1001
可以看出,除了第一次查看当前页面时等待网络加载数据的过程(输出:2001,1000是加载时间,1000是用户阅读时间),接下来的页面是立即返回(输出 1000 为用户阅读时间),完全无需等待。
代码是为了说明FutureTask的应用场景,请不要在项目中直接使用。
好的,我们到了,欢迎留下您的评论。