Ext 多线程

在 ExtJs 中支持多线程的类有 Ext.util.TaskRunner 和 Ext.util.DelayedTask。TaskRunner 提供了多线程的定时服务,DelayedTask 允许你延时多久在新建线程中执行一个任务。Ext.TaskMgr 是一个 TaskRunner 实例,在 TaskMgr.js 源码中可以看到最后一行是:

Ext.TaskMgr = new Ext.util.TaskRunner();

  实质上不管是 TaskRunner 还是 DelayedTask,它们都是通过 setInterval() 来执行任务的,TaskRunner 能多次重复的执行一个方法,而 DelayedTask 是延时执行完一次任务后就会调用 clearInterval() 来保证只执行一次。所以这里所说如何向任务的 run() 方法传递参数,本质上就是向 setInterval() 中方法传递参数。

  我们到 http://extjs.com/deploy/dev/docs/output/Ext.util.TaskRunner.html 看 TaskRunner 的 API,start(Object task) 中 task 参数是一个配置对象,这里关注它的两个属性:

  run : Function  定时执行的方法

  args : Array 传递给上面 run 方法的参数

  来看一个基本使用方法,下面是不带 args 属性参数的使用:

01.Ext.onReady(function(){
02.    var runner = new Ext.util.TaskRunner();
03.    runner.start({      //任务被调用的方法
04.        run: function(){
05.            alert('run() 方法被执行.')
06.        },
07.        interval: 1000, //一秒执行一次
08.        repeat: 5       //重复执行 5 次
09.    });
10.});

  上面没有向 run() 方法提供参数,那么应该如何向 run() 传入参数,run() 方法此时的原型是怎么样,又该如何获得传入的参数?办法是:js 函数的固有属性 arguments。看代码:

01.Ext.onReady(function(){
02.    var runner = new Ext.util.TaskRunner();
03.    runner.start({      //任务被调用的方法
04.        run: function(){ //run 方法原型不变,实际可以去遍历这个 arguments 参数数组
05.            alert('run() 方法被执行. 传入参数个数:' + arguments.length + ", 分别是:"
06.                + arguments[0] +"," + arguments[1] +"," + arguments[2]);
07.            return false;  //不返回 false,run() 方法会被永无止境的调用
08.        },
09.        args:[100,200,300],
10.        interval: 1000, //一秒执行一次,本例中 run() 只在 1 秒后调用一次
11.        repeat: 2       //重复执行 2 次, 这个参数已不再启作用了
12.    });
13.});

  如果不在 run() 方法中返回 false,你会发现会不停的弹出窗口,有了 args 属性时,repeat 根本不管用。原因还得从 TaskMgr 源代码中去发现:

01.if(t.interval <= itime){
02.    var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
03.    t.taskRunTime = now;
04.    if(rt === false || t.taskRunCount === t.repeat){
05.        removeTask(t);
06.        return;
07.    }
08.}
09.if(t.duration && t.duration <= (now - t.taskStartTime)){
10.    removeTask(t);
11.}

上面是 TaskRunner 的 runTasks 方法,当有了 args 参数,t.taskRunCount 永远都是 0,不会到达 t.taskRunCount === t.repeat,所以只能让 run() 方法返回 fasle 来终止,即满足 rt === false 时就会 removeTask(t),当然你也可以设定一个 duration 期限。

  想不太明白,为什么 ExtJs 不能让 args 和 repeat 同时有效,即让 ++t.taskRunCount 总是能得到执行。如果指定了 args 即传它,否则把当前被调用次数传递给 run() 方法。

  前面看到 run() 方法是通过 Function.apply(obj: Object, args: Array) 来调用的,它是通过数组来传递参数,方法中用 arguments 取得。JS 中另一调用函数的方法是 Function.call(obj: Object, arg1, arg2, arg3,...),相当于变长参数的形式。

01.Ext.onReady(function(){
02.    var runner = new Ext.util.TaskRunner();
03.    runner.start({      //任务被调用的方法
04.        run: function(arg){ //run 方法原型有变,有一个参数
05.            //同样能用 arguments 取到 args 中的所有元素
06.            alert('run() 方法被执行. 传入参数个数:' + arguments.length + ", 分别是:"
07.                + arguments[0] +"," + arguments[1] +"," + arguments[2] +
08.                ", arg 的值是:" + arg); //arg 对应 args 中的第一个元素
09.            return false;  //不返回 false,run() 方法会被永无止境的调用
10.        },
11.        args:[100,200,300],
12.        interval: 1000, //一秒执行一次,本例中 run() 只在 1 秒后调用一次
13.        repeat: 2       //重复执行 2 次, 这个参数已不再启作用了
14.    });
15.});

 

  关于 DelayedTask 传参数的用法也是同理,而且它还不存在 repeat 和 args  的不和谐之音。同样可有两种方式,arguments 数组中取和 run() 方法加个 arg 参数取得 args 的第一个元素。直接看代码:

1.var delayedTask = new Ext.util.DelayedTask();//你也可以在初始化时传入 fn,scope,args
2.delayedTask.delay(1000,function(arg){//没有 arg 参数也是能用 arguments 的
3.    //同样能用 arguments 取到 args 中的所有元素
4.    alert('run() 方法被执行. 传入参数个数:' + arguments.length + ", 分别是:"
5.            + arguments[0] +"," + arguments[1] +"," + arguments[2] +
6.            ", arg 的值是:" + arg); //arg 对应 args 中的第一个元素
7.     
8.},this,[100,200,300]);

  从 DelayedTask 中可看到它也是通过 apply 来调用 run() 方法的,fn.apply(scope, args || []); 没有指定参数则传空参。

### Java多线程文件下载实现方法 #### 使用单一线程进行文件下载 对于简单的场景,可以直接使用单一的`Thread`类来进行文件下载操作。然而这种方式效率较低,在面对多个文件下载需求时表现不佳。 ```java new Thread(() -> { try (InputStream in = new URL("http://example.com/file").openStream(); FileOutputStream out = new FileOutputStream("/local/path")) { byte[] buffer = new byte[4096]; int bytesRead; while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } System.out.println("下载完成"); } catch (IOException e) { e.printStackTrace(); } }).start(); ``` 此方式适用于少量文件的小规模应用环境[^1]。 #### 利用线程池优化多文件下载流程 当面临大量文件需同步获取的情况,频繁创建销毁线程会带来额外开销并可能造成资源浪费甚至耗尽可用线程数。因此推荐采用固定大小的线程池来控制并发度,通过提交任务线程池而不是直接启动新线程的方式处理请求。 ```java ExecutorService executor = Executors.newFixedThreadPool(5); // 创建具有五个工作线程线程池 for(int i=0;i<fileUrls.length;i++){ final String url=fileUrls[i]; Runnable worker=new DownloadTask(url,"/save/location/"+i+".ext"); executor.execute(worker); } executor.shutdown(); while (!executor.isTerminated()) { } System.out.println("所有下载已完成!"); ``` 上述代码片段展示了如何利用`Executors`工厂方法构建一个拥有指定容量的工作队列,并向其中添加若干个实现了Runnable接口的任务实例以异步执行文件下载逻辑[^2]。 #### 自定义分片下载提升速度 为了进一步加快大文件传输速率,还可以考虑将大型文件分割成更小的部分交给不同子进程分别读取写入磁盘,最后再合并这些部分得到完整的副本。这种方法特别适合网络带宽充足而服务器端支持断点续传协议的情形下实施。 ```java class SplitDownload implements Callable<Long> { private static final int BUFFER_SIZE = 8 * 1024; // 缓冲区大小设置为8KB @Override public Long call() throws Exception { HttpURLConnection conn=(HttpURLConnection)new URL(url).openConnection(); long startByte,endByte,contentLength; contentLength=conn.getContentLengthLong(); RandomAccessFile raf = null; InputStream is=null; try{ ... }finally{ if(is!=null){ is.close(); } if(raf!=null){ raf.close(); } } return endByte-startByte+1L; } } ``` 这段示例仅提供了一个简化版框架用于说明概念;实际项目里还需要加入异常捕获机制以及进度条更新等功能模块[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值