跳至主要内容

多线程真头疼,但也挺有趣


http://www.mitbbs.com/article_t/Java/31137799.html

发信人: rongxuer (蓉儿), 信区: Java
标  题: 多线程真头疼,但也挺有趣
发信站: BBS 未名空间站 (Sat Apr 27 12:17:18 2013, 美东)

有点点长啊。

有一个 tool, 以前处理的数据比较少,所以速度,时间都不成问题。但现在需要处理
的数据增大,速度问题就突出了。费了好大一番力气,总算弄通了,一次处理的时间由
原来的 24 个小时,减小为 3 个小时。

第一次弄多线程,很费劲。但弄通了(只是这个小 task 弄通了,并非多线程弄通了)
,又觉得很有意思,小有成就感。分享一下,希望对 beginner 有帮助,也希望有经验
的多指教。其中有些地方,还没有完全明白,正在学习-ing。

程序其实也简单,就是在单机上运行的一个程序。过去单线程的,即使在多 core 的机
器上运行,也只能利用处理器的少部分能力。现在就是要改为多线程,多核处理。

数据存在 10 个文本文件中,每个大约 40M.程序从每个文件逐行读取,逐行处理,很
多信息被保存到不同的数据结构中。最后输出处理报告,中间还要将部分处理信息输出
到一个 XML 文件中。

经过一番 google search 和研究,所做的改动包括:
1) 将原来的逐行读取,逐行处理,改为先将每个文件的所有行读到一个新建的
DataStore 中,这个结构比较简单。就是保持原来的每行文本信息,以及行号,和
getter, setter 等。每个都加上 synchronized
  class DataStore{
  }

2) 建立一个 thread pool,并将这个 DataStore 对象传到这个 pool 中。
   ExecutorService executor =    Executors.newFixedThreadPool(count);
            for (int i = 0; i < count; i++) {
                Runnable dataThread = new DataThread(dataStore,
                                                       "Thread " + i);
                executor.execute(dataThread);
            }
3) DataThread 的 run(),基本上就运行原来的代码,原来是从文件读取每一行,现在
变为从 dataStore 中逐行取得文本进行处理。

4)原来逐行处理,逐行输出部分信息到一个 XML 文件中,原来的记录顺序需要保持。
现在由于多线程异步处理,直接输出无法保证顺序。就先把这部分信息存储到一个新建
的 map 中,保存记录 ID -> data 的映射。当一个文件处理完了,才最后按 ID 从
map 有序输出到 XML 中。

5)其他很多信息需要计算,排序和保存。过去用的都是无保护的 Map, List, Set,以
及原始的 int. 后来在需要的地方,对这些数据存储对象都加上锁。比如:
   synchronized(dataMap){
      dataMap.remove(id);
   }

其他需要帮助处理结果的 private static 方法,也都加上了 synchronized.

6)很多 int 型的计算 (i.e. itemCounts++), 都从 int 变成了 AtomicInteger.

基本上就这样。下面是几点体会。
1)为了测试多线程下无线程保护时可能出现的问题。开始我故意对所有的那些 map,
list 和 set 都用原来的,无任何保护。一运行,果然,上面那个 dataMap 马上出错
。数据处理完后,抛出一个 RuntimeException, 提示数据状态 inconsistent.

2) 遇到一个困难。就是在 local 机器上测试时,虽然发现速度明显提高,50% 左右,
但是通过 OS 的 performance monitor  观察时,发现内存使用逐渐小幅增加,当数据
处理越 2/3 后,内存所剩已无几,而 cpu 也马上从多 core,变为过去的单 core.但
机器并没有停下来,而是按单 core 的速度,处理完剩下的 1/3, memory 此期间全负
荷运转。
  多线程的程序,怎么自动变成了单线程?memory usage 为什么增加? memory 增加
,其实预见到了,由于上面提到的改动 4),原来不保存,现在逐渐保存到一个 map 中
,所以消耗内存。没想到影响这么大,可能是记录比较多的缘故。再联系到 CPU 的使
用,原来当 memory 快耗尽时,OS or JVM 能够探测到,就自动 switch 到 single
core, 以保证程序继续运行。这里,还挺 smart 哈。

  后来,就为解决这个问题,费力了。数据再大一点,可能就会死机了。等一个文件处
理完,存储到 map 中的方案不行。还得在处理数据的同时,想办法输出,不保存,或
少保存信息到 map 中。可是,异步处理,如何保持输出顺序呢?

  try 了很多次,又 search 了很多,最后找到办法了。在原来 10 个 thread 的基础
上,再加一个专用的 thread, 在其他 10 thread 处理 dataStore 并存储到
resultMap 的同时,负责 concurrently 从这个 map 中输出信息到 XML 文件中;每当
一条信息输出完,就立刻从 map 中删除记录。这样,这个 map 的 size 一直会维持在
非常小的水平。如何控制顺序呢?就取一个循环变量。
  int i =0;
  do(
  {
     DataRecord record = resultMap.get(i);
     if( record != null ){
        // 1. output
       // 2. resultMap.remove(i)
       // i++
     }else{
        Thread.sleep(1000);
     }
  }while(i<dataStore.size() 

  觉得这段小程序真是体现了 multithread 的精髓。多人同时干一件事多或多件事,
并相互协调,保证很好的完成任务。主要的 10 个线程处理耗资源和时间的主任务,另
一个 thread 负责简单的输出;当它空闲无任务可做时,就通过睡觉 (sleep),稍事休
息,又继续工作,直到结束。fun!

3) 最后,又经过了一点优化吧。上面提到需要存取多个 map, list 和 set 时,都用
synchronized block 加锁。这样一来,有很多地方都需要这样,看上去不好。后来就
做了改动:
Map -> ConcurrentHashMap
List -> ConcurrentSkipListMap

只有一个 ArrayList 还是用 synchronized 加锁,因为不确定改为
CopyOnWriteArrayList 是否合适,还在研究! 

最终,速度又从 50% 进一步减少为原来的 1/8,内存问题也得到了解决。发现速度的
提高,基本上与 thread pool 中线程数量成比例的增加。当然,thread 的数量应该小
于所在机器 core 的数量?

使用 Executors.newFixedThreadPool(count) 这张方式,很容易测试单线程和多线的
区别,所需要改的就是改变那个 count 变量的值,很方便。

最后提一下,第一次在 debugger 里调试多线程,感觉难多了!



--

※ 修改:·rongxuer 於 Apr 27 12:27:44 2013 修改本文·[FROM: 99.]

评论

此博客中的热门博文

记者探访H&M实体店,店员:我们也爱国,希望明天就关门

http://www.mitbbs.com/article_t/ChinaNews/32684337.html 发信人: jiuna (), 信区: ChinaNews 标  题: 记者探访H&M实体店,店员:我们也爱国,希望明天就关门 发信站: BBS 未名空间站 (Thu Mar 25 02:55:55 2021, 美东) 3月25日中午,《环球时报》记者走访了北京市三里屯核心商区,探访新疆棉事件对H&M 及Nike等品牌实体店的影响。记者发现,在整整占据了三层的北京最大的H&M店之一的 店铺内,顾客稀少,门可罗雀,一些路人进店挑选后也选择不购买其产品即离开。 "看到了H&M和Nike的声明之后,近期不会再购买他们的产品了。既然想在中国做生意 ,还侮辱中国人,那中国人肯定不会支持,"一位路人在接受《环球时报》采访时表示 。"抵制他们的产品对我们并不会产生什么影响,因为替代产品有很多。国内很多品牌 都有很好的设计和质量,大多数时间也都在网上逛淘宝,样式和质量都比H&M好很多, "一位姓赵的北京居民表示。 在接受采访时,一些路人表示,除非H&M展现出道歉的诚意,否则将来也不会再选择这 个品牌。 位于上海某繁华商业街的一家HM店,今天门口有保安值守,店员十分警戒,不让记者拍 摄。两名店员对《环球时报》记者表示他们也希望明天就关店,也不希望发生这样的事 情,他们也是爱国的,但他们只是店员,希望得到记者理解。 王先生是一位在上海南京西路附近上班的打工族,由于听到关于HM的消息,他特地在午 休时间来HM店面转转。王先生告诉记者,"作为一家大公司应该吸取经验教训,在中国 做生意,想赚中国人的钱,就更应该尊重中国人民的感情。" 王先生告诉记者,中国老百姓应该拿出一点实际行动,给这些国外公司一些警告以及反 馈信息。王先生说希望这个事件能给更多的类似企业传递更多的信息,中国人民欢迎外 企来做生意,但是前提一定是秉持公平公正的原则,尊重中国人民的感情。 -- ...

分享一下在ICC工作的经验,以及contractor行业的注意事项。

http://www.mitbbs.com/article_t/JobHunting/33509585.html 发信人: poyang (), 信区: JobHunting 标  题: 分享一下在ICC工作的经验,以及contractor行业的注意事项。 发信站: BBS 未名空间站 (Fri May 22 02:28:24 2020, 美东) 最近由于疫情很多new grad同学没法上岸,很多一线大厂更是lay off出来一批大神, new grad的简历在hr眼里更是没法看了。所以很多同学都在考虑去ICC苟住身份,楼主 作为一个在icc工作了一年多的人,写下这篇帖子介绍一下ICC里的一些情况,希望在这 个非常时期帮助到各位还在找工作的同学。 1. 什么是ICC,ICC运作模式是怎么样的? ICC是India Consulting Company 的简称主要从事科技软件行业的外包业务。现在信息 时代每个行业都要像数字化方面转型,特别是大型企业。但除了一些大型科技公司,需 要大量稳定的Full time SDE。市场上大部分的传统行业包括医疗,金融, 通信,零售 等行业也需要大量技术支持和销售业务转型,所以也需要大量的码农。但为了维持公司 (client)的灵活性,所以只招收contractor(合同工). 这样可以简化招聘流程,不需要 承担contractor的身份问题,也可以随时进行人员精简,比如现在项目结束client可以 随时开除目前的合同工并且不给任何补助,也不需要给合同工任何福利。 一般来说Client公司不能直接到市场上招contractor,需要通过第三方公司(vendor) 来 招聘合同工,vendor手上握有大量client的招聘资源,但是手上不一定有很多 candidate 来应聘这些岗位。所以就会把资源放给别的公司(比如ICC)来收取一定的 提成。正常情况下大部分的公司都会给合同工$60~$90 一小时的rate, 但会被vendor抽 掉一部分,再被icc抽掉一部分,你能拿到手可能就只剩下$40~$70一小时。这个抽成具 体取决于icc和vend...

贡献一个485的详细清单,希望对大家有帮助

http://www.mitbbs.com/article_t/Immigration/33151393.html 发信人: gsu (niuer), 信区: Immigration 标  题: 贡献一个485的详细清单,希望对大家有帮助! 发信站: BBS 未名空间站 (Sat Jan 18 12:06:21 2014, 美东) 我们是一家三口,小孩小于14岁,签证都是从J1-waiver-H1B or H4, 希望对和我一样 情况的递交485时有所帮助,在必要时根据自己的情况调整。 主申请人: January 18, 2014 USCIS Texas Service Center 4141 North St. Augustine Road Dallas, TX 75227 Re:  Form I-485, Application to Adjust Status          Form I-765, Application for Employment Authorization          Form I-131, Application for Advance Parole        Applicant: **** (Primary Applicant) Dear Immigration Officer: I am filing Application to Adjust Status based on my approval for Form I-140 under classification 203(b)(1)(A) with receipt number **** My current status is H1B. Enclosed please for filing in the above referenced matter the followin...