首页 > 代码库 > 关于JAVA多线程的那些事__初心者

关于JAVA多线程的那些事__初心者

前言

  其实事情的经过也许会复杂了点,这事还得从两个月前开始说。那天,我果断不干IT支援。那天,我立志要做一个真正的程序猿。那天,我26岁11个月。那天,我开始看Android。那天,我一边叨念着有朋自远方来,一边投身了JAVA的怀抱。那天,一切将会改变。

  好吧,反正总的来说就是时隔4年半,我又开始搞JAVA了。Eclipse还是Eclipse;NetBeans还是NetBeans;Java被收之后已经来到了7,现在是8;在入手了几本JAVA的书籍后发现《JAVA编程思想》还是这么伟大;开始了新的路途--Android。

  下面可能会涉及到一些跟其他框架或者语言比较的情况,比如当下挺火的HTML5,比如越来越火的U3D,比如之前一直从事的WEB应用。然后是我的老朋友JAVA,还有他介绍给我认识的新朋友Android跟之前我或深或浅涉猎过的这些奇奇怪怪的东西做一些比较。当然,主要论述的都是JAVA关于线程这块的一些或技术或心路的历程吧。

 

我与多线程的那些事

  先不从项目说,先说说我对多线程的恐惧。遥想当年读书的时候,JAVA课程设计,老湿给了一个题目,我觉得没啥难度,然后自己立了一个题目,叫两人对战游戏,就是控制两个人跑来跑去然后有攻击防御和技能三个按键,反正就是很弱智的那种。然后老湿说要求用TXT来编,用cmd来调,给一个还是两个星期还是一个月时间去弄,反正我就在最后一个星期管这事。然后我拿着一本JAVA编程思想,两天就把UI写起来,然后就是地狱般的4天,各种各样稀奇古怪的BUG,debug之后触发了更多的bug,然后到了交任务的时候我已经燃烧殆尽了~ 然后由于当初没选老湿的题目已经让老湿很不爽,现在做的这个东西奇奇怪怪的当然被狠批一顿。给了个60多分了事。事后我自己努力了一把,然后就放弃了,删源码,删程序,各种删~ 然后各种自卑,各种觉得自己就是个哔哔,然后看到多线程就尿了~

  然后到了工作写的第一个系统,库存ERP系统(这里很感激公司能给一个实习生这么大的空间去发挥啊~)。忽略掉前面那些技术选型啊,噼里啪啦的东西之后到了数据访问这一环,我半天时间就写完了。用的是ASP.NET。但是这里出问题了,因为我用的是直接提交一条SQL语句去执行操作的,然后根据返回的值来判定后面执行的SQL操作。那时我突然想到一个问题,如果两个用户同时用我的SQL语句去操作,而操作的资源这个时候判定是不对的,但是可能由于另外一个用户的同时操作使得这个判定通过了,这怎么弄?(简单来说就是一个同步的问题,那时候还不知道~)

  然后就是.NET实现原子性啊,.NET实现事务啊噼里啪啦的东西。到了最后,我把业务逻辑写在了T-SQL存储过程里面,因此这里面的东西充斥着begin tran a; submit tran a; rollback tran a;然后还特意研究了T-SQL事务处理的递归性(具体可以参阅我之前博客的这篇文章:SQL存储过程递归下的事务处理(本身的缺陷还是蛮大的) )。然后各种噼里啪啦之后,写T-SQL各种顺手。然后各种各样的逻辑全部写存储过程,然后练就了一双写T-SQL的好手~~

  在往后是随着经验值的上涨,我开始密谋架构ERP内核的时候做的单例模式,用于管理用户登录信息。这个时候出现了一种叫线程安全的单例模式,然后就是lock(object){...},不求甚解,直接就上了。关于“线程安全的单例模式”不懂的可以直接复制去问度娘。那时做出来的时候特嚣张,虽然一知半解,也去忽悠别人了~ 反正是能跑,你管俺是真懂的还是蒙中的~ 

  直到……

 

初体验

  各种蛋疼的人事,各种无聊的纠纷,各种倔强的泪水之后,俺从事Android了,俺搞JAVA了~

  说下Android跟U3D UI线程方面的不同吧,纯个人或别人观点,有错的话请温柔地指出。U3D的UI线程我们公司的一位专门从事这个领域的哥们说U3D的线程就是一个大轮询,往里面塞任务。简单来说U3D的UI是跑一条线程的(其实想想也是合理的,因为U3D支持多平台发布,其中就包括了Web平台,而JAVAScript就是单线程的)。而Android的UI线程是多线程的,其他线程想要调用Android的UI线程,那就得用handler了,貌似是个钩子的方式做嵌入(详细情况可以参考这里: Android之Handler用法总结 还有钩子的介绍,设计模式之:模板模式)。那么从上面的信息得知Android的UI极有可能是维护一个线程池的。(纯属个人推断,未经考察证实)而Android除了上面的Handler(在其他线程跑UI)之外貌似还提供了一些不错的在UI内部跑其他线程的支援。

  然后虽然了解了上面的这些,但是项目的内核还得用JAVA多线程来做,而且跟UI目测没有半毛钱关系。因此吧,就只能老老实实打基础了。有个同事的QQ签名是:当你的才华不足以支撑你的野心时,那你就得多看点书。我觉得很有道理,因此我看书……

 

Runnable&Thread

  开始好多人都说JAVA多线程就是实现Runnable接口或者写Thread类。但是这种说法有时会混熬我们的思维,反正我那时候就是把这两者当成平衡的来看,但其实从现在的应用来说Runnable更多的应该比喻成一个任务(Task),而Thread是管理这个任务的容器。有兴趣的人可以去搜下Thread.start()和Runnable.run()之间的区别。因此很多写过多线程的程序员都会有这种感觉:Runnable用的比Thread要多。而在《JAVA编程思想》里面更是推荐大家用所谓的线程池来管理自己的线程。这里引出了一个概念:线程池。这里可以得出一个结论:线程的管理和线程本身已经分离了。因此这里叫线程已经显得不合适了,这时候换一个称呼可能更加的有助于我们对这个事情的理解。比如把一个Runnable的实例叫“任务”。

 

 1      private Runnable aNewTask = new Runnable() 2      { 3          @Override 4          public void run() 5          { 6              //跑一些奇怪的东西 7              System.out.println("哈哈"); 8          } 9      }10     ExecutorService exec = Executors.newCachedThreadPool();11     exec.execute(aNewTask);12     exec.shutdown();

 

  上述就是把一个任务aNewTask放到exec的一个池里面跑,shutdown是关闭池,当然也可以不关闭啦。池有很多种,具体可以看文档或者JAVA编程思想吧。

 

生产者-消费者模型

  这种模型适合的情景:一个资源,两条线程,一条线程负责读,一条线程负责写。

  具体可以参阅下面的文章: java实现生产者消费者问题 

  文章里面提到几种方法,我用到第三种,因此下面是基于JAVA的堵塞队列LinkedBlockingQueue来讨论的。这里的生产者-消费者模型说的比较少,但是他是我整个模型的核心思想。这里有几点我项目的时候犯过的错这里提出来一下:

  1. LinkedBlockingQueue里面我存的是一个数组,就是说这是一个数组的队列。而每当我从另外的地方接受了数据之后就传入这个队列,这时候需要把这个传入的数组clone()一次。因为直接传入的那只是一个引用,而非实体,极有可能这个引用的实体在递交给队列之后就会销毁了。这时就只剩下一个地址了。

  2. LinkedBlockingQueue是线程安全的,但是如果想要确保一次任务或者一个循环里面多次调用的时候不受干扰,那么还是得用同步锁synchronized去锁定循环体执行的代码。有个资深程序员,同时也是我同学,说:尽量不要用synchronized去锁定资源。而两一位更加资深的工程师也说锁会产生格外的损耗,能不用就不用。但是显然到了我项目里面还得要读写的时候锁死,否则数据就会刷的不整齐了~ 刷不整齐,小伙伴们又有意见了~ 现在想想,其实也真没有锁定的必要啊~ 至于数据会不会丢帧就要留给测试了~。

  3. 虽然跟这个模型不相干,但是还是记录一下。在实施的过程中,我机(dou)智(bi)地把类似于 exec.execute(aNewTask); 这种语句锁了。然后症状就是必须要等这个任务执行完了,这个锁别人的线程才会得到释放~。。可伶我这个一秒锁人家一下的社会青年啊~ 锁出翔了~

 

项目

  先上一张最终的收发设计图吧~

  

  中间那条竖线的意思是接受一个响应的时候才发送下一条数据,夜已深~ 不多介绍项目的细节了。因为整个项目的实施过程基本可以另外再起一篇长文了。比如最开始思考的无堵塞模型(这个非常好玩~~),然后后来编码第一版的基于Channel的发送堵塞模型,然后发现原来下面的东西没有想象中的强悍(羞~),做成这一版的基于Module的发送堵塞模型。好吧写到这里感觉已经在放嘲讽了~ 

 

总结

  1. 多线程很好玩,思考程序很开心。

  2. 帅气的喊一句,当手抚键盘的时候,吾已为神。

  3. 白赖先生听到之后投来一个看手(S)表(B)的眼神~

 

-- 原创的哟,转载请加出处哟 http://www.cnblogs.com/gssl/p/3854512.html 虽然没什么人转的哟 呵呵--