抓取网页生成电子书(继续并发专题~FutureTask有点类似Runnable的get方法支持阻塞)
优采云 发布时间: 2022-01-06 23:11抓取网页生成电子书(继续并发专题~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的应用场景,请勿直接在项目中使用。
好了,就这样了,欢迎大家留言。