为了体会守护线程的作用,我做了一个下载文件的demo,可以没事用来测测网速什么的,其特性如下1、一共有三个线程,分别是主线程,下载线程,守护线程2、主线程启动下载线程和守护线程3、下载线程连续下载100个文件,如果出现异常自动捕获并进入下一个文件的下载4、如果下载线程下载某个文件超过了30秒,就认为是超时,而这个超时的检测由守护线程执行5、如果守护线程发现某个文件下载超时,就停掉下载线程,并想办法另起一个新的下载线程继续下载首先我们不使用Java设置的守护线程,而是用一个普通线程充当“守护线程”,也就是设置一个计数器,下载成功一个文件就+1,加到100后这个伪“守护线程”就会自我终止,代码如下package zz.vc.qduedu; import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL;import javax.net.ssl.HttpsURLConnection; public class DownloadTest { public static long totalDownloadTime;//全部文件共消耗的下载时间 public static long downloadStartTime;//某个文件下载开始的时间 public static DownloadThread downloadThread; public static int completedFileCount = 0;//下载成功的文件数 public static final String url = "https://d25hz2nnwuc2tq.cloudfront.net/images/image/cache/data/2016/10/19/1476866804-800x450-1080x608.webp"; public static final int total_times = 100; public static void main(String[] args) throws IOException { // TODO Auto-generated method stub downloadThread = new DownloadThread(); downloadThread.start(); AlxDemonThread demonThread = new AlxDemonThread(); demonThread.start(); } public static class DownloadThread extends Thread{ public boolean shouldStop = false;//超时后终结此进程 public void run() { for(int i=completedFileCount;i<total_times;i++){ if(shouldStop)return; try { long cost = downloadNow(url, i); if(shouldStop)return; System.out.println("第"+i+"次耗时"+cost); } catch (Exception e) { // TODO: handle exception if(shouldStop)return; System.out.println("下载失败"); e.printStackTrace(); } if(shouldStop)return; completedFileCount++; totalDownloadTime += System.currentTimeMillis() - downloadStartTime; downloadStartTime = 0; } if(!shouldStop)System.out.println("总耗时=="+totalDownloadTime); } } public static class AlxDemonThread extends Thread{ @Override public void run() { // TODO Auto-generated method stub super.run(); while (2>1) { try { Thread.sleep(1000); // System.out.println("守护线程还活着"); } catch (InterruptedException e) { } if(completedFileCount == total_times)return; if(System.currentTimeMillis() - downloadStartTime > 30*1000){ // System.out.println("第"+alreadyTime+"超时了"); System.out.println("the "+completedFileCount+" time out of time"); downloadThread.shouldStop = true; downloadThread.interrupt(); downloadThread = new DownloadThread(); downloadThread.start(); completedFileCount++; totalDownloadTime += 30*1000; } } } } public static long downloadNow(String strUrl,int times) throws IOException{ downloadStartTime = System.currentTimeMillis(); URL url = new URL(strUrl); HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection(); httpURLConnection.setDoInput(true); httpURLConnection.setUseCaches(false); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); httpURLConnection.setRequestProperty("Charset", "UTF-8"); httpURLConnection.setRequestProperty("Content-Type","multipart/form-data;boundary=" + "******"); httpURLConnection.connect(); // System.out.println("响应码是::"+httpURLConnection.getResponseCode()); File pictureFile = new File("d:/"+times+".jpg"); BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream()); FileOutputStream outputStream = new FileOutputStream(pictureFile); byte[] buffer = new byte[1024]; int len = 0; BufferedOutputStream out = new BufferedOutputStream(outputStream); while ((len = bis.read(buffer)) != -1) { if(System.currentTimeMillis() - downloadStartTime > 30*1000)throw new RuntimeException("超时"); out.write(buffer, 0, len); } out.flush(); out.close(); bis.close(); return System.currentTimeMillis()-downloadStartTime; }}运行上面的程序,可以在控制台看见每次文件下载的耗时,如果出现了异常,并不会中断子线程,而是会开始下载下一个文件但是如果伪“守护线程”发现了某个文件下载超时,就会调用downloadTread.interrupt()方法停止当前的下载线程并且重新new一个下载线程继续之前的进度下载。在这个地方,我发现Thread.interrupt()方法并不会立即停止当前线程,当前线程会继续运行十几秒的时间 我的解决方法是修改Tread里的一个私有变量shouldStop,在跨过耗时操作之后检查这个私有变量,如果被修改过那么就return掉当前线程。 现在,我们斗胆把这个伪“守护线程”编程真的守护线程,用setDaemon(true)在start()之前实现。 public static void main(String[] args) throws IOException { // TODO Auto-generated method stub downloadThread = new DownloadThread(); downloadThread.start(); AlxDemonThread demonThread = new AlxDemonThread(); demonThread.setDaemon(true); demonThread.start(); }然后把我们用于停止伪“守护线程”的计数器去掉 //if(completedFileCount == total_times)return; 然后重新执行这个程序1、如果下载很顺畅没有任何一个文件超时,那么守护线程会随着下载线程的终止而终止2、如果有一个文件下载超时了,守护线程会自动new线程,但是这个线程和守护线程跑了没一会就停了,守护线程退出。得出如下几个结论1、守护线程由主线程创建,守护主线程,同样也守护主线程创建的所有子线程。2、主线程如果终止(包括crash),由主线程创建的子线程并不会终止,守护线程也不会终止。3、守护线程创建的所有线程都是守护线程,当父守护线程守护的线程终止,父守护线程及其创建的子守护线程都会一起停止第三条有点拗口,简单解释一下就是:当守护线程把下载超时的线程停止掉之后,这个下载线程并不会立即停掉,此时守护线程又创建了一个下载线程,而这个由守护线程创建的下载线程并不好用,它会莫名其妙的随着上个下载线程的终止而终止。原因就是这个新下载线程其实时候守护线程,守护主线程(其实就是守护父守护线程,也就间接守护旧的下载线程),所以没等下完就自动终止了。 那么看来通过简单粗暴的设置守护线程并不好用,那么我们根据“守护线程会守护创建它的线程”的原理,让下载线程创建一个线程守护它自己,当这个守护线程发现下载线程超时以后,通知主线程创建一个新的下载线程进行下载,同时也有一个新的守护线程守护它 import java.io.BufferedInputStream; import java.io.BufferedOutputStream; import java.io.DataOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.util.Random;import javax.net.ssl.HttpsURLConnection; public class CopyOfDownloadTest { public static long total; public static long startTime; public static int alreadyTime = 0; public static final String url = "https://d25hz2nnwuc2tq.cloudfront.net/images/image/cache/data/2016/10/19/1476866804-800x450-1080x608.webp"; public static final int total_times = 100; private static Random threadLock = new Random();//线程锁 public static void main(String[] args) throws IOException, InterruptedException { // TODO Auto-generated method stub new DownloadThread().start(); while (1<2) { synchronized (threadLock) {//让主线程持有线程锁 threadLock.wait();//锁住主线程 } System.out.println("主线程解锁,准备重新new下载线程"); new DownloadThread().start(); } } public static class DownloadThread extends Thread{ public boolean shouldStop = false;//超时后终结次进程 public void run() { AlxDemonThread demonThread = new AlxDemonThread(this); demonThread.setDaemon(true); demonThread.start(); for(int i=alreadyTime;i<total_times;i++){ if(shouldStop)return; try { long cost = downloadNow(url, i); if(shouldStop)return; System.out.println("第"+i+"次耗时"+cost); } catch (Exception e) { // TODO: handle exception if(shouldStop)return; System.out.println("下载失败"); e.printStackTrace(); } if(shouldStop)return; alreadyTime++; total += System.currentTimeMillis() - startTime; startTime = 0; } if(!shouldStop)System.out.println("总耗时=="+total); } } public static class AlxDemonThread extends Thread{ private DownloadThread mDownloadThread = null; public AlxDemonThread(DownloadThread t) { // TODO Auto-generated constructor stub this.mDownloadThread = t; } @Override public void run() { // TODO Auto-generated method stub super.run(); while (2>1) { try { Thread.sleep(1000); // System.out.println("守护线程还活着"); } catch (InterruptedException e) { } if(alreadyTime == total_times)return; if(System.currentTimeMillis() - startTime > 30*1000){ System.out.println("第"+alreadyTime+"超时了"); mDownloadThread.shouldStop = true; mDownloadThread.interrupt(); alreadyTime++; total += 30*1000; synchronized (threadLock) { threadLock.notify(); } return;//停掉守护线程,防止再new一个子线程 } //因为是守护线程,所以在下载线程结束后会自动停止 } } } public static long downloadNow(String strUrl,int times) throws IOException{ startTime = System.currentTimeMillis(); URL url = new URL(strUrl); HttpsURLConnection httpURLConnection = (HttpsURLConnection) url.openConnection(); httpURLConnection.setDoInput(true); httpURLConnection.setUseCaches(false); httpURLConnection.setRequestMethod("GET"); httpURLConnection.setRequestProperty("Connection", "Keep-Alive"); httpURLConnection.setRequestProperty("Charset", "UTF-8"); httpURLConnection.setRequestProperty("Content-Type","multipart/form-data;boundary=" + "******"); httpURLConnection.connect(); // System.out.println("响应码是::"+httpURLConnection.getResponseCode()); File pictureFile = new File("d:/speed/test"+times+".jpg"); BufferedInputStream bis = new BufferedInputStream(httpURLConnection.getInputStream()); FileOutputStream outputStream = new FileOutputStream(pictureFile); byte[] buffer = new byte[1024]; int len = 0; BufferedOutputStream out = new BufferedOutputStream(outputStream); while ((len = bis.read(buffer)) != -1) { if(System.currentTimeMillis() - startTime > 30*1000)throw new RuntimeException("超时"); out.write(buffer, 0, len); } out.flush(); out.close(); bis.close(); return System.currentTimeMillis()-startTime; }}上面的程序中,守护线程由下载子线程创建,守护下载子线程。当守护线程发现下载线程超时之后,就会终止下载线程,然后通知主线程再重新创建一个下载线程继续之前的下载,然后这个守护线程会随着要终止的下载线程一起结束。一对新的下载线程和守护线程继续当前的下载任务。但是上面的程序在下载完成之后并不会退出,因为主线程还在wait()中。所有的下载子线程和守护线程均退出。从这里可以得出一个结论:1、object.wait()和object.notify必须在synchronized(object){}代码块中执行,否则会报java.lang.IllegalMonitorStateException异常 2、一个线程wait()之后并不会结束。并且守护它的守护线程也不会结束。3、主线程也可以wait()4、主线程出现crash崩溃了之后,主线程开启的子线程会继续执行。由主线程产生的守护线程会守护主线程产生的子线程。本文永久更新链接地址 :http://www.linuxidc.com/Linux/2016-10/136491.htm
收藏该网址