Android线程池控制并发数多线程下载
程序员文章站
2022-04-12 11:45:59
多线程下载并不是并发下载线程越多越好,因为当用户开启太多的并发线程之后,应用程序需要维护每条线程的开销,线程同步的开销。
这些开销反而会导致下载速度降低。因此需要避免在代...
多线程下载并不是并发下载线程越多越好,因为当用户开启太多的并发线程之后,应用程序需要维护每条线程的开销,线程同步的开销。
这些开销反而会导致下载速度降低。因此需要避免在代码中直接开启大量线程执行下载。
主要实现步奏:
1、定义一个downutil类,下载工作基本在此类完成,在构造器中初始化ui线程的handler。用于子线程和ui线程传递下载进度值。
2、所有的下载任务都保存在linkedlist。在init()方法中开启一个后台线程,不断地从linkedlist中取任务交给线程池中的空闲线程执行。
3、每当addtask方法添加一个任务,就向 mpoolthreadhandler发送条消息,就从任务队列中取出一个任务交给线程池执行。这里使用了使用了semaphore信号量,也就是说只有当一个任务执行完成之后,release()一个信号量,才能从linkedlist中取出一个任务再去执行,否则acquire()方法会一直阻塞线程,直到上一个任务完成。
public class downutil { //定义下载资源的路径 private string path; //指定下载文件的保存位置 private string targetfile; //定义下载文件的总大小 private int filesize; //线程池 private executorservice mthreadpool; //线程数量 private static final int default_thread_count = 5; //任务队列 private linkedlist<runnable> mtasks; //后台轮询线程 private thread mpoolthread; //后台线程的handler private handler mpoolthreadhandler; //ui线程的handler private handler muithreadhandler; //信号量 private semaphore semaphore; private semaphore mhandlersemaphore = new semaphore(0); //下载线程数量 private int threadnum; public downutil(string path , string targetfile , int threadnum , final progressbar bar) { this.path = path; this.targetfile = targetfile; this.threadnum = threadnum; init(); muithreadhandler = new handler() { int sumsize = 0; @override public void handlemessage(message msg) { if (msg.what == 0x123) { int size = msg.getdata().getint("upper"); sumsize += size; log.d("sumsize" , sumsize + ""); bar.setprogress((int) (sumsize * 1.0 / filesize * 100)); } } }; } private void init() { mpoolthread = new thread() { public void run() { looper.prepare(); mpoolthreadhandler = new handler() { public void handlemessage(message msg) { if (msg.what == 0x111) { mthreadpool.execute(gettask()); try { semaphore.acquire(); } catch (interruptedexception e) { e.printstacktrace(); } } } }; mhandlersemaphore.release(); looper.loop(); } }; mpoolthread.start(); mthreadpool = executors.newfixedthreadpool(default_thread_count); mtasks = new linkedlist<>(); semaphore = new semaphore(default_thread_count); } public void download() { try { url url = new url(path); httpurlconnection conn = (httpurlconnection) url.openconnection(); conn.setconnecttimeout(5 * 1000); conn.setrequestmethod("get"); conn.setrequestproperty( "accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " + "application/x-shockwave-flash, application/xaml+xml, " + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " + "application/x-ms-application, application/vnd.ms-excel, " + "application/vnd.ms-powerpoint, application/msword, */*"); conn.setrequestproperty("accept-language", "zh-cn"); conn.setrequestproperty("charset", "utf-8"); conn.setrequestproperty("connection", "keep-alive"); //得到文件的大小 filesize = conn.getcontentlength(); conn.disconnect(); int currentpartsize = filesize / threadnum + 1; randomaccessfile file = new randomaccessfile(targetfile , "rw"); file.setlength(filesize); file.close(); for (int i = 0 ; i < threadnum ; i++) { //计算每条线程下载的开始位置 int startpos = i * currentpartsize; //每条线程使用一个randomaccessfile进行下载 randomaccessfile currentpart = new randomaccessfile(targetfile , "rw"); //定位该线程的下载位置 currentpart.seek(startpos); //将任务添加到任务队列中 addtask(new downthread(startpos , currentpartsize , currentpart)); } } catch (ioexception e) { e.printstacktrace(); } } private runnable gettask() { if (!mtasks.isempty()) { return mtasks.removefirst(); } return null; } private synchronized void addtask(runnable task) { mtasks.add(task); try { if (mpoolthreadhandler == null) { mhandlersemaphore.acquire(); } } catch (interruptedexception e) { e.printstacktrace(); } mpoolthreadhandler.sendemptymessage(0x111); } private class downthread implements runnable { //当前线程的下载位置 private int startpos; //定义当前线程负责下载的文件大小 private int currentpartsize; //当前线程需要下载的文件块 private randomaccessfile currentpart; //定义该线程已经下载的字节数 private int length; public downthread(int startpos , int currentpartsize , randomaccessfile currentpart) { this.startpos = startpos; this.currentpartsize = currentpartsize; this.currentpart = currentpart; } @override public void run() { try { url url = new url(path); httpurlconnection conn = (httpurlconnection) url.openconnection(); conn.setconnecttimeout(5 * 1000); conn.setrequestmethod("get"); conn.setrequestproperty( "accept", "image/gif, image/jpeg, image/pjpeg, image/pjpeg, " + "application/x-shockwave-flash, application/xaml+xml, " + "application/vnd.ms-xpsdocument, application/x-ms-xbap, " + "application/x-ms-application, application/vnd.ms-excel, " + "application/vnd.ms-powerpoint, application/msword, */*"); conn.setrequestproperty("accept-language", "zh-cn"); conn.setrequestproperty("charset", "utf-8"); conn.setrequestproperty("connection", "keep-alive"); inputstream instream = conn.getinputstream(); //跳过startpos个字节 skipfully(instream , this.startpos); byte[] buffer = new byte[1024]; int hasread = 0; while (length < currentpartsize && (hasread = instream.read(buffer)) > 0) { currentpart.write(buffer , 0 , hasread); //累计该线程下载的总大小 length += hasread; } log.d("length" , length + ""); //创建消息 message msg = new message(); msg.what = 0x123; bundle bundle = new bundle(); bundle.putint("upper" , length); msg.setdata(bundle); //向ui线程发送消息 muithreadhandler.sendmessage(msg); semaphore.release(); currentpart.close(); instream.close(); } catch (exception e) { e.printstacktrace(); } } } public static void skipfully(inputstream in , long bytes) throws ioexception { long remaining = bytes; long len = 0; while (remaining > 0) { len = in.skip(remaining); remaining -= len; } } }
以下是mainactivity的代码:
public class mainactivity extends activity { edittext url; edittext target; button downbn; progressbar bar; downutil downutil; private string savepath; @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.main); //获取界面中的四个界面控件 url = (edittext) findviewbyid(r.id.address); target = (edittext) findviewbyid(r.id.target); try { file sdcarddir = environment.getexternalstoragedirectory(); savepath = sdcarddir.getcanonicalpath() + "/d.chm"; } catch (exception e) { e.printstacktrace(); } target.settext(savepath); downbn = (button) findviewbyid(r.id.down); bar = (progressbar) findviewbyid(r.id.bar); downbn.setonclicklistener(new view.onclicklistener() { @override public void onclick(view view) { downutil = new downutil(url.gettext().tostring() , target.gettext().tostring() , 7 , bar); new thread() { @override public void run() { try { downutil.download(); } catch (exception e) { e.printstacktrace(); } } }.start(); } }); } }
页面布局比较简单这里一并贴出:
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <textview android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/title1"/> <edittext android:id="@+id/address" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/address"/> <textview android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/targetaddress"/> <edittext android:id="@+id/target" android:layout_width="match_parent" android:layout_height="wrap_content"/> <button android:id="@+id/down" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="@string/down"/> <!-- 定义一个水平进度条,用于显示下载进度 --> <progressbar android:id="@+id/bar" android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" style="?android:attr/progressbarstylehorizontal"/> </linearlayout>
此例主要是在李刚老师的《疯狂java的讲义》的多线程的例子上修改,感谢李刚老师,如有不足之处,欢迎批评指正。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。