【多线程】多线程的项目实战

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/wrs120/article/details/90634863

  需求是这样的:最近在做一个有关学校考试的项目,其中老师可能任课好几门学科,每个学科有好几个班上,考完试后,老师想打印学生答题试卷(因为是上机考试),即涉及到了批量打印试卷,并且下载时将这些试卷打成一个压缩包,此功能用到了多线程

1.为什么用多线程

  充分利用cpu资源

2.什么时候用多线程

  1. 高并发: 系统接受实现多用户多请求的高并发时,通过多线程来实现。
  2. 线程后台处理大任务: 一个程序是线性执行的。如果程序执行到要花大量时间处理的任务时,那主程序就得等待其执行完才能继续执行下面的。那用户就不得不等待它执行完。这时候可以开线程把花大量时间处理的任务放在线程处理,这样线程在后台处理时,主程序也可以继续执行下去,用户就不需要等待。线程执行完后执行回调函数。
  3. 大任务: 大任务处理起来比较耗时,这时候可以起到多个线程并行加快处理(例如:分片上传)。比如处理一个for循环时要花费大量时间,就可以考虑多线程了

3.多线程编码项目实战

先说一下此功能用到的知识点:

  1. 线程池
  2. 线程
  3. CountDownLatch
//1.CountDownLatch,计数
CountDownLatch doneSignal = new CountDownLatch(1000);

//2.创建线程池来存放线程,以防考生数量太多创建太多线程,占用过多资源
ThreadPoolExecutor executor = new ThreadPoolExecutor(50, 150, 60000, TimeUnit.MILLISECONDS, new LinkedBlockingDeque<>());

for (ExaminationModel examineeModel : notExamStudentInfo) {
	。。。。。。
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
           。。。。。。打印试卷的逻辑
            } finally {             
            	// 每执行完一个线程,数量减1      
                doneSignal.countDown();
            }
        }
    });
    //3. 将任务添加到线程池
    executor.execute(thread);
}

//  4. 为了让所有的试卷都生成之后再执行压缩以及删除PDF文件,所以需要让主线程等待子线程执行完之后再执行await
try {
	// 主线程等待
    doneSignal.await();
    System.out.println("线程运行时间:" + (System.currentTimeMillis() - startTime) + "ms");
} catch (InterruptedException e) {
    log.error(e.getMessage(), e);
}

//5. 关闭线程池(所有线程执行完关闭线程池)
executor.shutdown();

// 6. 将所有试卷打包zip
boolean flag = fileToZip();

实现思路:
  创建线程池,和线程锁计数器,每打印一份试卷就是一个任务,将次任务添加到线程池中,然后线程执行,当所有的试卷都打印完,将所有的试卷打包成zip

知识点讲解:

  1. 之所以用线程池,是因为防止产生过多的线程,从而造成线程来回切换,而造成的形成损失
  2. CountDownLatch:它的作用是允许1或N个线程等待其他线程完成执行,在这用它是因为 要把所有的试卷,打包成zip压缩包,所以主线程要等所有的子线程即生成试卷要执行完才能打包,用到了CountDownLatch的await()和countDown()方法,有关CountDownLatch更多讲解,参见:https://www.cnblogs.com/skywang12345/p/3533887.html
  3. 我这创建的核心线程为50,最大线程为150,更具电脑cpu性能设置,执行程序打印线程号,线程号一直超不过50,是因为任务队列使用的是LinkedBlockingDeque,大小为Integer.MAX_VALUE(Integer.MAX_VALUE=7fffffff(十六进制) = 2147483647(十进制)),任务队列一直没有满,具体原因详情参见:https://mp.csdn.net/mdeditor/90082697#

4. 为什么不用join()

使用join()可能出现的问题:

  1. join()后面的代码可能提前完成,这样打包的数据就不全了
  2. join()过程中可能被打断了,这样系统就会抛异常(因为底层调用的是wait()),如果不想让程序判断就得做各种异常的判断,比较麻烦

猜你喜欢

转载自blog.csdn.net/wrs120/article/details/90634863