[java多线程] - Thread&Runnable运用

时间:2021-12-01 17:34:37

  负载是一个很大的话题,也是一个非常重要的话题。不管是在大的互联网软件中,还是在一般的小型软件,都对负载有一定的要求,负载过高会导致服务器压力过大;负载过低又比较浪费服务器资源,而且当高请求的时候还可能出现低效率的问题。多线程就是一种提高服务效率的方式。面对海量的用户请求,单线程肯定扛不住,那么多线程的需求也就应运而生,所以说掌握多线程的开发技术对于技术人员来说肯定是非常重要的。参考文档http://docs.oracle.com/javase/7/docs/api/


一、Runnable使用

publicinterface Runnable {
   public abstract void run();
}

    Runnable接口中,只有一个方法,就是run方法。该方法的主要作用是执行用户需要执行的代码。也就是说我们可以将我们需要在多线程环境下执行的代码放到run里面。

public class RunnableDemo {
    static SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    public static void main(String[] args) {
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + sdf.format(new Date()));
                try {
                    Thread.sleep(1000);// 休息1s
                } catch (InterruptedException e) {
                }
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + sdf.format(new Date()));
            }
        }).start();

        System.out.println("主线程:" + Thread.currentThread().getName() + ":" + sdf.format(new Date()));
    }
}

// 结果
/*
子线程:Thread-0:2015-08-27 18:25:14
主线程:main:2015-08-27 18:25:14
子线程:Thread-0:2015-08-27 18:25:15
*/

  二、Thread使用

  这个类是Java中的线程基础类,基本上多线程开发不管是直接或者间接均需要依赖上该类。主要介绍几个方法:

  1、start方法: 该方法是线程执行入口,如果你需要你新建的线程开始执行,那么请调用start方法。

  2、run方法:线程执行具体代码,放用户希望执行的代码。和start的区别是,调用start方法是使用多线程方法执行,如果调用run方法,那就是单线程执行。也就是说start方法是启动线程,run方法是执行具体代码。在上面的那个例子中,如果是调用run,而不是start方法,那么一定是先打印出来两个子线程的输出值,再打印主线程的输出值。

  3、sleep方法:使当前线程休眠指定时间,参数为休眠的时间,单位毫秒。

  4、interrupt方法: 中断线程的休眠(sleep, join等操作),会产生InterruptedException异常。

[java多线程] - Thread&Runnable运用[java多线程] - Thread&Runnable运用
public class InterruptThreadDemo {
    public static void main(String[] args) {
        Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
                try {
                    Thread.sleep(2000); // 休息2s
                } catch (InterruptedException e) {
                    ThrowableUtil.logThrowable(e); // 打印异常
                }
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
            }
        });
        t1.start();

        // 中断线程
        t1.interrupt();

        System.out.println("主线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
    }
}
InterruptThreadDemo

    5、isAlive&isInterrupted&isDaemon方法:检测是否处于运行状态或者是否被中断或者判断是否守护模式运行。

   6、join方法:将其他线程的执行嵌入到当前线程中,等待嵌入线程执行完毕,该线程才会被唤醒进行执行。

[java多线程] - Thread&Runnable运用[java多线程] - Thread&Runnable运用
public class JoinThreadDemo {
    public static void main(String[] args) {
        final Thread t1 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
                try {
                    Thread.sleep(5000); // 休息5s
                } catch (InterruptedException e) {
                    ThrowableUtil.logThrowable(e); // 打印异常
                }
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
            }
        }, "1");
        Thread t2 = new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
                // 等待线程1执行完,再继续执行
                try {
                    t1.join();
                } catch (InterruptedException e) {
                    ThrowableUtil.logThrowable(e);
                }
                System.out.println("子线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
            }
        }, "2");
        t1.start();
        t2.start();

        System.out.println("主线程:" + Thread.currentThread().getName() + ":" + TimeUtil.nowTime());
    }
}

// 结果
/*
子线程:2:2015-08-27 18:52:29 457
主线程:main:2015-08-27 18:52:29 457
子线程:1:2015-08-27 18:52:29 457
子线程:1:2015-08-27 18:52:34 458
子线程:2:2015-08-27 18:52:34 458
*/
JoinThreadDemo

   7、setDaemon方法:设置为守护模式,当主线程结束后,被设置为守护模式的线程自动结束。

  8、currentThread方法:获取当前线程。

三、多线程的实际应用

  多线程的应用是比较多的,比如说针对请求分别创建多个线程服务,下载等等。这里就弄一个下载美眉图片的小demo。

[java多线程] - Thread&Runnable运用[java多线程] - Thread&Runnable运用
  1 import java.io.BufferedReader;
  2 import java.io.File;
  3 import java.io.InputStream;
  4 import java.io.InputStreamReader;
  5 import java.net.URL;
  6 import java.net.URLConnection;
  7 import java.util.ArrayList;
  8 import java.util.List;
  9 import java.util.Map;
 10 import java.util.concurrent.BlockingQueue;
 11 import java.util.concurrent.ConcurrentHashMap;
 12 import java.util.concurrent.ConcurrentMap;
 13 import java.util.concurrent.LinkedBlockingDeque;
 14 import java.util.regex.Matcher;
 15 import java.util.regex.Pattern;
 16 
 17 import javax.imageio.stream.FileImageOutputStream;
 18 import javax.imageio.stream.ImageOutputStream;
 19 
 20 import com.gerry.bd.util.ThrowableUtil;
 21 
 22 public class BeautyGirlPhotoDownloadDemo {
 23     public static void main(String[] args) {
 24         String[] categorys = new String[] { "rihan", "yule", "dongm" };
 25         ConcurrentMap<String, BlockingQueue<String>> map = new ConcurrentHashMap<String, BlockingQueue<String>>();
 26 
 27         // 分别启用线程来获取图片的url
 28         for (String category : categorys) {
 29             BlockingQueue<String> queue = new LinkedBlockingDeque<>();
 30             map.put(category, queue); // 添加一个初始化
 31             Thread thread = new Thread(new FetchImageUrlRunnable(category, queue), "fetchimg_" + category);
 32             thread.start();
 33         }
 34 
 35         File imagePath = new File("D:/image/");
 36         // 每一个品类其两个线程去下载
 37         for (Map.Entry<String, BlockingQueue<String>> entry : map.entrySet()) {
 38             for (int i = 0; i < 2; i++) {
 39                 Thread thread = new Thread(new DownloadImageRunnable(imagePath, entry.getKey(), entry.getValue()), "downloadimage_" + entry.getKey() + i);
 40                 thread.start();
 41             }
 42         }
 43     }
 44 
 45     /**
 46      * 解析页面代码,保存图片url链接
 47      * 
 48      * @author jsliuming
 49      * 
 50      */
 51     public static class FetchImageUrlRunnable implements Runnable {
 52         private String category;
 53         private BlockingQueue<String> imageUrlQueue;
 54 
 55         public FetchImageUrlRunnable(String category, BlockingQueue<String> queue) {
 56             this.category = category;
 57             this.imageUrlQueue = queue;
 58         }
 59 
 60         @Override
 61         public void run() {
 62             try {
 63                 String url = "";
 64                 BufferedReader br = null;
 65                 for (int i = 10; i < 100; i++) {
 66                     for (int j = 1; j < 20; j++) {
 67                         url = "http://www.mm8mm8.com/" + this.category + "/list_" + i + "_" + j + ".html";
 68                         System.out.println(Thread.currentThread().getName() + ":" + url);
 69 
 70                         StringBuffer content = new StringBuffer();
 71                         try {
 72                             URLConnection connection = new URL(url).openConnection();
 73                             connection.connect();
 74                             br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
 75                             String line = null;
 76                             while ((line = br.readLine()) != null) {
 77                                 content.append(line);
 78                             }
 79                         } catch (Exception e) {
 80                             ThrowableUtil.logThrowable(e);
 81                         } finally {
 82                             if (br != null) {
 83                                 try {
 84                                     br.close();
 85                                 } catch (Exception e) {
 86                                 }
 87                             }
 88                         }
 89 
 90                         // 已经拿到内容,开始解析url
 91                         if (content.length() == 0) {
 92                             // empty
 93                             break;
 94                         } else {
 95                             // 开始解析
 96                             for (String u : this.getHtmlImageUrlList(content.toString())) {
 97                                 this.imageUrlQueue.put(u);
 98                             }
 99                         }
100                     }
101                 }
102             } catch (Throwable e) {
103                 ThrowableUtil.logThrowable(e);
104             }
105         }
106 
107         /**
108          * 获取图片url
109          * 
110          * @param htmlText
111          * @return
112          */
113         private List<String> getHtmlImageUrlList(String htmlText) {
114             List<String> list = new ArrayList<String>();
115             Pattern pattern = Pattern.compile("<img\\s*src\\s*=\\s*\"(?<imgUrl>[^\\s\"'<>]*)\"");
116             Matcher matcher = pattern.matcher(htmlText);
117             while (matcher.find()) {
118                 list.add(matcher.group("imgUrl"));
119             }
120             return list;
121         }
122     }
123 
124     /**
125      * 下载图片用线程
126      * 
127      * @author jsliuming
128      * 
129      */
130     public static class DownloadImageRunnable implements Runnable {
131         private String category;
132         private BlockingQueue<String> imageUrlQueue;
133         private File baseFile;
134 
135         public DownloadImageRunnable(File path, String category, BlockingQueue<String> queue) {
136             this.category = category;
137             this.imageUrlQueue = queue;
138             baseFile = new File(path, this.category);
139         }
140 
141         @Override
142         public void run() {
143             int index = 0;
144             InputStream input = null;
145             ImageOutputStream ios = null;
146             while (true) {
147                 try {
148                     String imgurl = this.imageUrlQueue.take();
149 
150                     URLConnection connection = new URL(imgurl).openConnection();
151                     connection.connect();
152                     input = connection.getInputStream();
153                     ios = new FileImageOutputStream(new File(baseFile, Thread.currentThread().getId() + "_" + index++ + ".jpg"));
154                     byte[] buf = new byte[2048];
155                     int n = -1;
156                     while ((n = input.read(buf)) > 0) {
157                         ios.write(buf, 0, n);
158                     }
159                 } catch (Throwable e) {
160                     ThrowableUtil.logThrowable(e);
161                 } finally {
162                     if (input != null) {
163                         try {
164                             input.close();
165                         } catch (Exception e) {
166                         }
167                     }
168                     if (ios != null) {
169                         try {
170                             ios.close();
171                         } catch (Exception e) {
172                         }
173                     }
174                 }
175             }
176         }
177 
178     }
179 }
BeautyGirlPhotoDownloadDemo .java

  这个代码没有关闭的设置,所有在下载完成后,需要手动关闭。后期会改吧 。。。。。

 [java多线程] - Thread&Runnable运用

[java多线程] - Thread&Runnable运用

 

===========================================================

使用CountDownLatch来控制下载线程下载完数据后结束程序,代码如下:

[java多线程] - Thread&Runnable运用[java多线程] - Thread&Runnable运用
  1 import java.io.BufferedReader;
  2 import java.io.File;
  3 import java.io.InputStream;
  4 import java.io.InputStreamReader;
  5 import java.net.URL;
  6 import java.net.URLConnection;
  7 import java.util.ArrayList;
  8 import java.util.List;
  9 import java.util.Map;
 10 import java.util.concurrent.BlockingQueue;
 11 import java.util.concurrent.ConcurrentHashMap;
 12 import java.util.concurrent.ConcurrentMap;
 13 import java.util.concurrent.CountDownLatch;
 14 import java.util.concurrent.LinkedBlockingDeque;
 15 import java.util.concurrent.TimeUnit;
 16 import java.util.regex.Matcher;
 17 import java.util.regex.Pattern;
 18 
 19 import javax.imageio.stream.FileImageOutputStream;
 20 import javax.imageio.stream.ImageOutputStream;
 21 
 22 import com.gerry.bd.util.ThrowableUtil;
 23 
 24 public class BeautyGirlPhotoDownloadDemo {
 25     static volatile boolean fetchTheradRunning = true; // 用于控制结束线程
 26     static final CountDownLatch latch = new CountDownLatch(3); // 用于控制结束线程
 27 
 28     public static void main(String[] args) {
 29         String[] categorys = new String[] { "rihan", "yule", "dongm" };
 30         ConcurrentMap<String, BlockingQueue<String>> map = new ConcurrentHashMap<String, BlockingQueue<String>>();
 31 
 32         // 分别启用线程来获取图片的url
 33         for (String category : categorys) {
 34             BlockingQueue<String> queue = new LinkedBlockingDeque<>();
 35             map.put(category, queue); // 添加一个初始化
 36             Thread thread = new Thread(new FetchImageUrlRunnable(category, queue), "fetchimg_" + category);
 37             thread.start();
 38         }
 39 
 40         File imagePath = new File("D:/image/");
 41         // 每一个品类其两个线程去下载
 42         for (Map.Entry<String, BlockingQueue<String>> entry : map.entrySet()) {
 43             for (int i = 0; i < 2; i++) {
 44                 Thread thread = new Thread(new DownloadImageRunnable(imagePath, entry.getKey(), entry.getValue()), "downloadimage_" + entry.getKey() + i);
 45                 thread.start();
 46             }
 47         }
 48 
 49         new Thread(new Runnable() {
 50             @Override
 51             public void run() {
 52                 try {
 53                     latch.await();// 等待完成
 54                     fetchTheradRunning = false;
 55                 } catch (Throwable e) {
 56                     ThrowableUtil.logThrowable(e);
 57                 }
 58             }
 59         }).start();
 60     }
 61 
 62     /**
 63      * 解析页面代码,保存图片url链接
 64      * 
 65      * @author jsliuming
 66      * 
 67      */
 68     public static class FetchImageUrlRunnable implements Runnable {
 69         private String category;
 70         private BlockingQueue<String> imageUrlQueue;
 71 
 72         public FetchImageUrlRunnable(String category, BlockingQueue<String> queue) {
 73             this.category = category;
 74             this.imageUrlQueue = queue;
 75         }
 76 
 77         @Override
 78         public void run() {
 79             try {
 80                 String url = "";
 81                 BufferedReader br = null;
 82                 for (int i = 10; i < 1024; i++) {
 83                     for (int j = 1; j < 21; j++) {
 84                         url = "http://www.mm8mm8.com/" + this.category + "/list_" + i + "_" + j + ".html";
 85                         System.out.println(Thread.currentThread().getName() + ":" + url);
 86 
 87                         StringBuffer content = new StringBuffer();
 88                         try {
 89                             URLConnection connection = new URL(url).openConnection();
 90                             connection.connect();
 91                             br = new BufferedReader(new InputStreamReader(connection.getInputStream()));
 92                             String line = null;
 93                             while ((line = br.readLine()) != null) {
 94                                 content.append(line);
 95                             }
 96                         } catch (Exception e) {
 97                             ThrowableUtil.logThrowable(e);
 98                         } finally {
 99                             if (br != null) {
100                                 try {
101                                     br.close();
102                                 } catch (Exception e) {
103                                 }
104                             }
105                         }
106 
107                         // 已经拿到内容,开始解析url
108                         if (content.length() == 0) {
109                             // empty
110                             break;
111                         } else {
112                             // 开始解析
113                             for (String u : this.getHtmlImageUrlList(content.toString())) {
114                                 this.imageUrlQueue.put(u);
115                             }
116                         }
117                     }
118                 }
119 
120                 // 完成后,通知
121                 latch.countDown();
122             } catch (Throwable e) {
123                 ThrowableUtil.logThrowable(e);
124             }
125         }
126 
127         /**
128          * 获取图片url
129          * 
130          * @param htmlText
131          * @return
132          */
133         private List<String> getHtmlImageUrlList(String htmlText) {
134             List<String> list = new ArrayList<String>();
135             Pattern pattern = Pattern.compile("<img\\s*src\\s*=\\s*\"(?<imgUrl>[^\\s\"'<>]*)\"");
136             Matcher matcher = pattern.matcher(htmlText);
137             while (matcher.find()) {
138                 list.add(matcher.group("imgUrl"));
139             }
140             return list;
141         }
142     }
143 
144     /**
145      * 下载图片用线程
146      * 
147      * @author jsliuming
148      * 
149      */
150     public static class DownloadImageRunnable implements Runnable {
151         private String category;
152         private BlockingQueue<String> imageUrlQueue;
153         private File baseFile;
154 
155         public DownloadImageRunnable(File path, String category, BlockingQueue<String> queue) {
156             this.category = category;
157             this.imageUrlQueue = queue;
158             baseFile = new File(path, this.category);
159         }
160 
161         @Override
162         public void run() {
163             int index = 0;
164             InputStream input = null;
165             ImageOutputStream ios = null;
166             while (fetchTheradRunning || this.imageUrlQueue.size() > 0) {
167                 try {
168                     String imgurl = null;
169                     while (true) {
170                         imgurl = this.imageUrlQueue.poll(10, TimeUnit.SECONDS); // 阻塞10秒
171                         if (imgurl != null || !fetchTheradRunning) {
172                             break;
173                         }
174                     }
175                     if (imgurl == null) { // 如果url为空,那么再次循环
176                         continue;
177                     }
178 
179                     URLConnection connection = new URL(imgurl).openConnection();
180                     connection.connect();
181                     input = connection.getInputStream();
182                     ios = new FileImageOutputStream(new File(baseFile, Thread.currentThread().getId() + "_" + index++ + ".jpg"));
183                     byte[] buf = new byte[2048];
184                     int n = -1;
185                     while ((n = input.read(buf)) > 0) {
186                         ios.write(buf, 0, n);
187                     }
188                 } catch (Throwable e) {
189                     ThrowableUtil.logThrowable(e);
190                 } finally {
191                     if (input != null) {
192                         try {
193                             input.close();
194                         } catch (Exception e) {
195                         }
196                     }
197                     if (ios != null) {
198                         try {
199                             ios.close();
200                         } catch (Exception e) {
201                         }
202                     }
203                 }
204             }
205         }
206     }
207 }
BeautyGirlPhotoDownloadDemo2