




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、线程是Java的一大特色,从语言上直接支持线程,线程对于进程来讲的优势在于创建的代价很小,上下文切换迅速,当然其他的优势还有很多,缺点也是有的,比如说对于开发人员来讲要求比较高,不容易操作,但是Java的线程的操作已经简化了很多,是一个比较成熟的模型。很多时候,我们都用不到线程,但是当我们有一天不走运(或者走运)的时候,我们必须要面对这个问题的时候,应该怎么办呢?本文是我的学习笔记和一些总结,试图解决这个问题,引领还没有接触过Java 线程的开发人员进入一个Java线程的世界,其实很多东西在网路上已经有朋友总结过了,不过我感觉没有比较循序渐进,要么太基础,要么太高深,所以这边我由浅到深的总结一
2、下。但是很显然,我的资历尚浅,能力也很有限,如果有什么错误还望不吝赐教!麻烦发送mail到: 而且,这些大部份的都有源码,如果需要也可以发mail到这个邮箱,真的非常希望有人能指正我的错误!(一) 基本的API介绍1. 如何创建一个可以执行的线程类 创建一个线程有两个办法:继承Thread类或者实现Runnable接口。 首先:继承Thread类 这里一般只
3、需要我们来重写run这个方法。下面是代码:public class SimpleThread extends Thread public SimpleThread() start(); Override
4、0; public void run() while (true) System.out.println(this);
5、; / Imply other thread can run now, but we cannot assume that it will / work well every time, ac
6、tually , most of time we can get the same / result, but not to a certainty.
7、60; / yield(); try
8、; sleep(100); catch (InterruptedException e)
9、60; e.printStackTrace(); 其次:
10、实现Runnable接口,代码如下: Public class Inner implements Runnable private Thread thread; public Inner(String name)
11、 thread = new Thread(this, name); thread.start();
12、; public void run() while (true) &
13、#160; try
14、160; Thread.sleep(10); catch (InterruptedException e)
15、 throw new RuntimeException(e);
16、; 2. 几个常用的API这边介绍几个常见而且重要的的线程API,这边JDK文档有更加详细的说明,其实J
17、DK的文档就是个很好的学习资料,常备很重要哦!方法说明start使线程开始执行,实际上这个方法会调用下面的run这个方法,如果这个线程已经开始执行,则会扔出IllegalThreadStateExceptionsleep是当前已经运行的线程休眠一段时间。如果当前线程已经被别的线程中断的话,将会扔出InterruptedException,而且interrupted标志也会被清空。这个方法有两个版本,具体参看JDK文档。run线程执行的业务逻辑应该在这里实现。join等待另外一个线程死亡。如果当前线程已经被别的线程中断的话,将会扔出InterruptedException,而且interrupt
18、ed标志也会被清空。yield使当前线程临时的中断执行,来允许其他线程可以执行,因为Java的线程模型实际上映射到操作系统的线程模型,所以对于不同的操作系统,这个方法的就有不同的意义。对于非抢占式Operating System,这个方法使得其他线程得到运行的机会,但是对于抢占式的OS,这个方法没有太多的意义。关于这个方法,后边还有更多的介绍。waitWait方法和后边的两个方法都来自Object。看过Java源码的可以知道,这三个方法都是Native方法,使比较直接的和操作系统打交道的方法。这个方法的作用是让当前线程等待,直到被唤醒或者等待的时间结束。当前线程进入等待队列的时候,会放弃当前所
19、有的资源,所以当前线程必须获得这些对象的Monitor,否则会扔出IllegalMonitorStateException 关于wait方法的更多,后边会有介绍到。notify通知其他线程可以使用资源了。这个方法的使用要求很多,总之需要当前线程获得被调用的notify方法的对象的monitor。比如:
20、; synchronized (person)
21、 person.notify(); &
22、#160; 其实,获得monitor的方法还有别的,这里简单介绍一下:1. 执行这个对象的一个同步的方法2.
23、 执行这个对象的同步块3. 执行一个同步的静态方法notifyAll除了通知所有的线程可以准备执行之外,跟上面的方法要求一样。但是只有一个线程会被选择然后执行,这个就跟优先级和其他状态有关系了。interrupt中断线程。这边只是介绍了几个常用的API,但是非常重要,其他的API可以查看JDK的相关文档。但是在操作系统的概念中,很显然,对于一个线程应该还有别的状态,对,确实还有,但是Java在实现的映射的时候,也实现
24、了这些方法,只是不赞成使用,下面的主题将讨论这些方法以及这些方法的替代方法。3. 已经不赞成使用的方法对于一些不应该再使用的东西,有时候被称为反模式antipattern。这些都是概念上的东西,对于我们开发人员来讲,需要做的就是写出好的代码。方法说明stop强制使当前的线程停止执行。实际上,作为开发人员我们会意识到,线程的复杂程度是没有边际的,而这个方法这样武断的停止一个线程,必然导致问题产生。也就是说,这个方法天生就有问题。比如说一个线程掌握了很多对象,并且改变了其中一些的状态,如果突然当前对象突然被停止,将会释放这些对象的monitor,这个时候被改变状态的对象就是
25、被损坏的对象,其他线程再来操作的时候问题就出来了。替代的办法就是让当前线程正常结束,不使用这个方法。就是我们设置一些标志,如果这些标志满足的时候,我们结束线程。下面用JDK的例子: private Thread blinker; public void start() blinker = new Thread(this); blinker.start();
26、 public void stop() blinker.stop(); / UNSAFE! public void run() Thread thisThread = Thread.currentThread();
27、60; while (true) try thisThread.sleep(interval); catch (Interrup
28、tedException e) /do something 修改后: private volatile Thread blinker; public void stop()
29、160; blinker = null; public void run() Thread thisThread = Thread.currentThread(); /Check the flag w
30、hile (blinker = thisThread) try thisThread.sleep(interval); catch (Interrupt
31、edException e) /do something 当然如果这个方法中间有wait方法的调用的话,而且正在等待,我们可以使用这个办法来结束:Thread.currentThread().interrupt();然后处理InterruptedEx
32、ception 这个我也实现了避免使用stop方法的一个类,在源码中可以看到。suspend这个方法天生就有导致死锁的可能。如果当前线程持有很多对象的锁,但是当他suspend的时候,这些锁是不会释放的,想想就知道应该什么可能会发生,所以这个方法应该尽量不用。这里我们有办法替代这个方法,其实根替代stop的方法差不多,就是用wait方法来实现挂起,而不是事实上的挂起。比如:Override SuppressWarnings("static-access") &
33、#160; public void run()
34、 while (true)
35、0; try
36、; Thread.currentThread().sleep(1000);
37、; / Double check
38、160; if (isSuspended)
39、160; synchronized (this)
40、; while (isSuspended)
41、160;
42、160; wait();
43、
44、;
45、0;
46、60; catch (InterruptedException e) &
47、#160; / null
48、
49、0; 这样做可以同样实现挂起,但是仍然会释放资源。resume很显然,这个方法和上面的方法是对应的,所以上面用了wait方法来替代,所以这边应该用notify这个方法或者notifyAll这个方法来替代。 其实这边可以把上面的实现方式结合起来,实现一个可以安全stop和suspend的线程。这个在我的源码里有实现,但是不知道是不是对的。不过感觉原则应该没有问
50、题,那就是设置标志来结束或者挂起线程,而不是使用这些过时的方法。4. 跟线程相关的关键字跟线程相关的关键字我能够想到的就下面两个:关键字说明volatile这个关键字告诉编译器不要对这个属性或者值进行优化,也就是为了保证这个变量的同步性,如果这个值被更新,其他线程应该可以马上访问到最新的值,而不是“脏值”。其实这个关键字是同步互斥的一个模型,但是现在没有实现。synchronized给对象或者Class加锁,分为对象锁和Class锁。对象锁只是加在当前对象上,对别的对象没有影响,这种加锁一般都是把这个关键字用在方法和同步块上面。Class锁就是加在这个Class上面,所
51、有的其他对象想访问这个Class的对象的任何同步方法,必须获得这个锁。这种锁一般把这个关键字用在静态方法中,或者显示的这样实现: synchronized (AClass.class)
52、60; while (isSuspended)
53、 wait();
54、0;
55、60; 一般我们很少用Class锁。 这里简单提一下monitor,个人感觉这里把monitor认为是一把锁也可以。网络上有朋友解释的比较好:在java中,每个对象只有一个相应的monitor,一个mutex,而每一个monitor都可以有多个“doors”可以进入,即,同一个monitor中被守护的代码可以在不同的地方,因为同一个对象可以出现在不同的代码段,只要mute
56、x锁定的对象是同一个,每个入口都用Synchronized关键字表明,当一个线程通过了Synchronized关键字,它就所住了该monitor所有的doors其实线程的使用不在于语言的API,而在于对操作系统的理解和一些常见的调度算法,其实个人理解经验比较重要,后边介绍到线程的实现模式和设计模式。其实我还是以前的想法:对于语言的学习,首先学习语法和API,然后学习如何使用这些API在语法的框架内编写出高效的程序。很显然,模式就是实现后边的重要方法。模式常见的分类有实现模式、设计模式和架构模式。这里限于本人的能力问题,没有理解到架构上面去,所以这里只是研究了前两个。(二) 线程实现模式实现模式
57、这边主要参考自Effective Java这本书,至少分类是,但是很多内容应该会很不相同,当然还有Think in java。Effective Java是短小精悍的一本书,其中有太多的Java的关于实现模式的建议,但是这边把这本书的内容归类到实现模式,是我个人的想法,如果有什么不正确,万望指正。但是,个人认为这些概念性的东西仍然不会损害到我们需要讨论的问题的实质。1. 共享数据同步上面有提到过synchronized关键字,这个关键字保证一段代码同时只能有一个线程在执行,保证别人不会看到对象处于不
58、一致的状态中。对象将从一种一致的状态转变到另一种一致的状态,后来的线程将会看到后一种状态。在Java中,虚拟机保证原语类型(除double和long)的读写都是原子性的。即不需要同步,但是如果不对这样的数据读写进行同步,那么后果将很严重。可以参照Effective Java的解释,这里还要简单的提示意下,Effective Java中有提到double check这种方式,而且我的源代码中多次用到这种方法,单是需要提醒一下,如果用这种方式来实现singleton的话,就不可以了,因为这样有可能导致不完整的对象被使用,单是源码中的double check用的都是原语类型,所以OK。 这边的建议是
59、如果修改原语类型或者非可变类的属性,可以同步或者使用volatile关键字。如果是其他对象,必须同步。关于尽量少使用同步,这边的建议是,我们这样的初学者在不知道如何优化的情况下就不要优化,我们要的是正确的程序,而不是快的程序。2. wait方法的使用wait方法是一个很重要的方法,前面有介绍过这个方法,不但可以使一个线程等待,而且可以作为实现suspend的替代方法的一个方法。Wait方法的标准使用方式如下:
60、0; synchronized (obj)
61、160; while (condition)
62、160; wait();
63、 这里,对应wait方法还有一个notify和notifyAll方法,到底我们应该如何使用这两个方法来唤醒等待的线程呢?很显然notifyAll的使用是最安全的,但是会带来性能的降低。这里又提到我们初学者,应该优先考虑这个方法,而不是notify。3. 不要依赖线程调度器,管好自己的事情Thread.yield这个方法并不能保证线程的公平运行,所以这个方法不应该依赖。还有就是线程的优先级,Java的线程优先级有10个
64、等级,但是这个等级几乎没有什么用处,所以我们也不应该依赖这个优先级来控制程序,当然仍然可以优化一些服务,但是不能保证这些服务一定被优化了。我们应该尽量控制对critical resources的方法线程数,而不是用优先级或者yield来实现对资源的访问。4. 不要使用线程组线程组是一个过时的API,所以不建议使用。但是也不是一无是处,“存在即合理”嘛!(三) 线程设计模式什么是模式呢?Martin Flower先生这样描述:一个模式,就是在实际的上下文中,并且在其他上下文中也会有用的想法。这边的线程
65、设计模式大部分参考自林信良先生的设计模式,还有一些网路的文章,这些都是前辈们在使用线程时候的经验,非常值得我们借鉴。还有就是林信良先生的设计模式非常通俗易懂,是入门级选手的最佳选择。关于线程的模式应该还有别的,只是我这边现在只能总结这么多了,能力有限。这边用大量的UML来描述这些模式,但是由于我的UML学的不好,而且工具用的不怎么熟,画的图应该会有些问题,当做草图来看就好了。1. Single Threaded Execution这个模式在Java里说的话有点多余,但是这边还是先拿这个开胃一下。很明
66、显,从字面的意思,就是说同一时刻只有一个线程在执行,Java里用synchronized这个关键字来实现这个模式。确实多余 L!看看UML吧!其实用这个图来描述有点不好。其实应该用别的图来描述会比较好!比如协作图。2. Guarded Suspension网上有一个比较好的描述方式:要等我准备好噢!这里我们假设一种情况:一个服务器用一个缓冲区来保存来自客户端的请求,服务器端从缓冲区取得请求,如果缓冲区没有请求,服务器端线程等待,直到被通知有请求了,而客户端负责发送请求。很显然,我们需要对缓冲区进行保
67、护,使得同一时刻只能有一个服务器线程在取得request,也只能同一时刻有一个客户端线程写入服务。用UML描述如下:具体实现可以参看代码。但是,这个模式有一点点瑕疵,那就是缓冲区没有限制,对于有的情况就不会合适,比如说您的缓冲区所能占用的空间受到限制。下面的Producer Consumer Pattern应该会有所帮助。3. Producer ConsumerProducer Consumer跟上面的Guarded Suspension很像,唯一的区别在于缓冲区,Guarded Suspensio
68、n模式的缓冲区没有限制,所以,他们适用的场合也就不一样了,很显然,这个考虑应该基于内存是否允许。Producer Consumer的缓冲区就像一个盒子,如果装满了,就不能再装东西,而等待有人拿走一些,让后才能继续放东西,这是个形象的描述。可以参考下面的UML,然后具体可以参看源码。4. Worker ThreadWorker Thread与上面的Producer-consumer模式的区别在于Producer-consumer只是专注于生产与消费,至于如何消费则不管理。其实Worker Thread
69、模式是Producer-consumer与Command模式的结合。这边简单描述一下Command pattern。用UML就和衣很清晰的描述Command pattern。这个模式在我们的很多MVC框架中几乎都会用到,以后我也想写一个关于Web应用的总结,会提到具体的应用。其实Command pattern模式的核心就是针对接口编程,然后存储命令,根据客户短的请求取得相应的命令,然后执行,这个跟我们的Web请求实在是太像了,其实Struts就是这样做的,容器相当于Client,然后控制器Servlet相当于Invoker,Action相当于ICommand,那么Receiver相当于封装在A
70、ction中的对象了,比如Request等等。上面描述过Command pattern之后,我们回到Worker模式。这边看看worker的UML:从图中可以看到,CommandBuffer这个缓冲区不仅仅能够存储命令,而且可以控制消费者WorkerThread。这就是Worker模式。下面的Sequence应该会更加明确的描述这个模式,具体可以参看代码。5. Thread-Per-MessageThread-Per-Message模式是一个比较常用的模式了,如果我们有一个程序需要打开一个很大的文件
71、,打开这个文件需要很长的时间,那么我们就可以设计让一个线程来一行一行的读入文件,而不是一次性的全部打开,这样从外部看起来就不会有停顿的感觉。这个模式Future模式一起学习。6. Read-Write-Lock考虑这样一种情况:有一个文件,有很多线程对他进行读写,当有线程在读的时候,不允许写,这样是为了保证文件的一致性。当然可以很多线程一起读,这个没有问题。如果有线程在写,其他线程不允许读写。如果要比较好的处理这种情况,我们可以考虑使用Read-Write-Lock模式。这个模式可以如下描述:其实这
72、个模式的关键在于锁实现,这里有个简单的实现如下:public class Lock private volatile int readingReaders = 0; SuppressWarnings("unused") private volatile int writingWriters = 0; &
73、#160; SuppressWarnings("unused") private volatile int waitingWriters = 0; public synchronized void lockRead() try &
74、#160; while (writingWriters > 0 | waitingWriters > 0)
75、 wait(); catch (InterruptedException e) &
76、#160; / null readingReaders+;
77、 public synchronized void unlockRead() readingReaders-; notifyAll();
78、160; public synchronized void lockWrite() waitingWriters+; try &
79、#160; while (writingWriters > 0 | readingReaders > 0)
80、 wait(); catch (InterruptedException e) &
81、#160; / null finally wa
82、itingWriters-; writingWriters+; public synchronized void u
83、nlockWrite() writingWriters-; notifyAll(); 其实在锁里还可以添加优先级之类的控制。 7. &
84、#160; FutureFuture模式是Proxy模式和Thread-Per-Message模式的结合。考虑下面的情况:比如我们的word文档,里头有很多图片在末尾,我们打开这个文档的时候会需要同时读取这些图片文件,但是很明显,如果刚刚开始就全部读取进来的话会消耗太多的内存和时间,使得显示出现停顿的现象。那么我们应该怎么做呢,我们可以做这样一个对象,这个对象代表需要读入的图片,把这个对象放在图片的位置上,当需要显示这个图片的时候,我们才真正的填充这个对象。这个就是Proxy模式了。当然Proxy不仅仅是这么个意思,Proxy的真正意思是我们之需要访问Proxy来操作我们真正需要操作的对象,以便实现对客户段的控制。这边先简单描述一下Proxy模式:当Client请求的时候,我们用Proxy代替RealObject载入,当Client真正需要getObject的时候,Proxy将调用Rea
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 2025新款礼仪商务租车服务合同
- 2025深圳房屋租赁合同协议范本
- 加热炉检修安全培训课件
- 采掘支护及质量标准化标准培训
- 学校心理学-第十三章教学设计
- 属地安全管理
- 轴向柱塞泵介绍
- 企业培训之火灾隐患的认定
- 人力师培训课件个体心理与行为的分析
- 2025知识产权合同技术研发委托协议书
- 推进中国教育数字化的战略与政策
- 生育服务证办理承诺书
- 地下室顶板预留洞口施工方案标准版
- 航天航空科普知识竞赛考试题库及答案(共400多题)
- 第章脂肪酸的分解代谢
- 2022年宁夏粮食和物资储备局所属事业单位考试真题及答案
- 川09J139 居住建筑油烟气集中排放建筑构造(DBJT20-65)
- 浙江工商大学论文答辩汇报通用ppt模板
- 2023年北京市高中力学竞赛决赛试题
- C++反汇编与逆向分析技术揭秘(第2版)
- 2023届湖北省武汉市高三毕业生4月调考英语试卷及参考答案
评论
0/150
提交评论