ImageVerifierCode 换一换
格式:DOCX , 页数:33 ,大小:191.84KB ,
资源ID:28150239      下载积分:3 金币
快捷下载
登录下载
邮箱/手机:
温馨提示:
快捷下载时,用户名和密码都是您填写的邮箱或者手机号,方便查询和重复下载(系统自动生成)。 如填写123,账号就是123,密码也是123。
特别说明:
请自助下载,系统不会自动发送文件的哦; 如果您已付费,想二次下载,请登录后访问:我的下载记录
支付方式: 支付宝    微信支付   
验证码:   换一换

加入VIP,免费下载
 

温馨提示:由于个人手机设置不同,如果发现不能下载,请复制以下地址【https://www.bdocx.com/down/28150239.html】到电脑端继续下载(重复下载不扣费)。

已注册用户请登录:
账号:
密码:
验证码:   换一换
  忘记密码?
三方登录: 微信登录   QQ登录  

下载须知

1: 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。
2: 试题试卷类文档,如果标题没有明确说明有答案则都视为没有答案,请知晓。
3: 文件的所有权益归上传用户所有。
4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
5. 本站仅提供交流平台,并不能对任何下载内容负责。
6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

版权提示 | 免责声明

本文(Java多线程编程基础.docx)为本站会员(b****8)主动上传,冰豆网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对上载内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知冰豆网(发送邮件至service@bdocx.com或直接QQ联系客服),我们立即给予删除!

Java多线程编程基础.docx

1、Java多线程编程基础Java多线程设计模式 - wait/notify机制通常,多线程之间需要协调工作。例如,浏览器的一个显示图片的线程 displayThread想要执行显示图片的任务,必须等待下载线程downloadThread将 该图片下载完毕。如果图片还没有下载完,displayThread可以暂停,当 downloadThread完成了任务后,再通知displayThread“图片准备完毕,可 以显示了”,这时,displayThread继续执行。以上逻辑简单的说就是:如果条件不满足,则等待。当条件满足时,等待该 条件的线程将被唤醒。在Java中,这个机制的实现依赖于wait/no

2、tify。等待机 制与锁机制是密切关联的。例如:synchronized(obj) while(!condition) obj.wait();obj.doSomething();当线程A获得了obj锁后,发现条件condition不满足,无法继续下一处理,于 是线程A就wait()。在另一线程B中,如果B更改了某些条件,使得线程A的condition条件满足了 ,就可以唤醒线程A:synchronized(obj) condition = true;obj.notify();需要注意的概念是:调用obj的wait(), notify()方法前,必须获得obj锁,也就是必须写在 synchron

3、ized(obj) . 代码段内。调用obj.wait()后,线程A就释放了obj的锁,否则线程B无法获得obj锁, 也就无法在synchronized(obj) . 代码段内唤醒A。当obj.wait()方法返回后,线程A需要再次获得obj锁,才能继续执行。如果A1,A2,A3都在obj.wait(),则B调用obj.notify()只能唤醒A1,A2,A3中 的一个(具体哪一个由JVM决定)。obj.notifyAll()则能全部唤醒A1,A2,A3,但是要继续执行obj.wait()的下 一条语句,必须获得obj锁,因此,A1,A2,A3只有一个有机会获得锁继续执行, 例如A1,其余的需

4、要等待A1释放obj锁之后才能继续执行。当B调用obj.notify/notifyAll的时候,B正持有obj锁,因此,A1,A2,A3虽 被唤醒,但是仍无法获得obj锁。直到B退出synchronized块,释放obj锁后, A1,A2,A3中的一个才有机会获得锁继续执行。Java多线程编程基础之线程和多线程随着计算机技术的发展,编程模型也越来越复杂多样化。但多线程编程模型 是目前计算机系统架构的最终模型。随着CPU主频的不断攀升,X86架构的硬件已 经成为瓶,在这种架构的CPU主频最高为4G。事实上目前3.6G主频的CPU已经接近了顶峰。如果不能从根本上更新当前CPU的架构(在很长一段时间

5、内还不太可能),那么 继续提高CPU性能的方法就是超线程CPU模式。那么,作业系统、应用程序要发挥 CPU的最大性能,就是要改变到以多线程编程模型为主的并行处理系统和并发式 应用程序。所以,掌握多线程编程模型,不仅是目前提高应用性能的手段,更是下一代 编程模型的核心思想。多线程编程的目的,就是最大限度地利用CPU资源 ,当某一线程的处理不需要占用CPU而只和I/O,OEMBIOS等资源打交道时, 让需要占用CPU资源的其它线程有机会获得CPU资源。从根本上说,这就是多线程 编程的最终目的。第一需要弄清的问题如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对 象和线程的区别。线程

6、对象是可以产生线程的对象。比如在java平台中Thread对象,Runnable 对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对 象的start()开始,运行run方法体中的那一段相对独立的过程。鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质 区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明 白它们所代表的真实含义。天下难事必始于易,天下大事必始于细。让我们先从最简单的单线程来入手:(1)带引号说明只是相对而 言的单线程,(2)基于java。class BeginClasspublic static void main(St

7、ring args)for(int i=0;i100;i+)System.out.println(Hello,World!);如果我们成功编译了该java文件,然后在命令行上敲入:java BeginClass现在发生了什么呢?每一个java程序员,从他开始学习java的第一分钟里都 会接触到这个问题,但是,你知道它到底发生发什么?JVM进程被启动,在同一个JVM进程中,有且只有一个进程,就是它自己。然 后在这个JVM环境中,所有程序的运行都是以线程来运行。JVM最先会产生一个主 线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main方法开 始运行。当main方法结束后,主线程运

8、行完成。JVM进程也随之退出。我们看到的是一个主线程在运行main方法,这样的只有一个线程执行程序逻 辑的流程我们称之为单线程。这是JVM提供给我们的单线程环境,事实上,JVM底层还至少有 垃圾回收这样的后台线程以及其它非java线程,但这些线程对我们而言不可访问 ,我们只认为它是单线程的。主线程是JVM自己启动的,在这里它不是从线程对象产生的。在这个线程中, 它运行了main方法这个指令序列。理解它,但它没有更多可以研究的内容。接触多线程class MyThread extends Threadpublic void run()System.out.println(Thread say:He

9、llo,World!);public class MoreThreadspublic static void main(String args)new MyThread();new MyThread().start();System.out.println(Main say:Hello,World);执行这个程序,main方法第一行产生了一个线程对象,但并没有线程启动。main方法第二行产生了一个线程对象,并启动了一个线程。main方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句 。我们先不研究Thread对象的具体内容,稍微来回想一下上面的两个概念,线 程对象和线程。在JAVA

10、中,线程对象是JVM产生的一个普通的Object子类。而线 程是CPU分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说 一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要 急,但你要记得它们不是一回事就行了。累了吧?为不么不继续了?基于这种风格来介绍多线程,并不是每个人都喜欢和接受的,如果你不喜欢 ,正好不浪费你的时间了,而如果你接受的话,那就看下一节吧。Java多线程编程基础之线程对象在进入java平台的线程对象之前,基于基础篇(一)的一些问题,我先插入两个基本概念。线程的并发与并行在单CPU系统中,系统调度在某一时刻只能让一个线程运行,虽然这种调试机制有多

11、种形式(大多数是时间片轮巡为主),但无论如何,要通过不断切换需要运行的线程让其运行的方式就叫并发(concurrent)。而在多CPU系统中,可以让两个以上的线程同时运行,这种可以同时让两个以上线程同时运行的方式叫做并行(parallel)。在上面包括以后的所有论述中,请各位朋友谅解,我无法用最准确的词语来定义储如并发和并行这类术语,但我以我的经验能通俗地告诉大家它是怎么一回事,如果您看到我说的一些标准文档上说的不一样,只要意思一致,那您就不要挑刺了。JAVA线程对象现在我们来开始考察JAVA中线程对象。在JAVA中,要开始一个线程,有两种方式。一是直接调用Thread实例的start()方法

12、,二是将Runable实例传给一个Thread实例然后调用它的start()方法。在前面已经说过,线程对象和线程是两个完全不同的概念。这里我们再次深入一下,生成一个线程的实例,并不代表启动了线程。而启动线程是说在某个线程对象上启动了该实例对应的线程,当该线程结束后,并不会就立即消失。对于从很多书籍上可以看到的基础知识我就不用多说了。既然是基础知识,我也着重于从普通文档上读不到的内容。所以本节我重点要说的是两种线程对象产生线程方式的区别。class MyThread extends Thread public int x = 0; public void run() for(int i=0;i1

13、00;i+) try Thread.sleep(10); catch(Exception e) System.out.println(x+); 如果我们生成MyThread的一个实例,然后调用它的start()方法,那么就产生了这个实例对应的线程:public class Test public static void main(String args) throws Exception MyThread mt = new MyThread(); mt.start(); 不用说,最终会打印出0到99,现在我们稍微玩一点花样:public class Test public static voi

14、d main(String args) throws Exception MyThread mt = new MyThread(); mt.start(); System.out.println(101); 也不用说,在基础篇(一)中我们知道由于单CPU的原因,一般会先打印101,然后打印0到99。不过我们可以控制线程让它按我们的意思来运行:public class Test public static void main(String args) throws Exception MyThread mt = new MyThread(); mt.start(); mt.join(); Sys

15、tem.out.println(101); 好了,我们终于看到,mt实例对应的线程(假如我有时说mt线程请你不要怪我,不过我尽量不这么说)。在运行完成后,主线程才打印101。因为我们让当前线程(这里是主线程)等待mt线程的运行结束。在线程对象a上调用join()方法,就是让当前正在执行的线程等待线程对象a对应的线程运行完成后才继续运行。 请大家一定要深刻理解并熟记这句话,而我这里引出这个知识点的目的是为了让你继续看下面的例子:public class Test public static void main(String args) throws Exception MyThread mt =

16、 new MyThread(); mt.start(); mt.join(); Thread.sleep(3000); mt.start(); 当线程对象mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程对象上启动线程。结果我们看到:Exception in thread main java.lang.IllegalThreadStateException也就是这种线程对象一时运行一次完成后,它就再也不能运行第二次了。我们可以看一下它有具体实现:public synchronized void start() if (started) throw new IllegalThreadS

17、tateException(); started = true; group.add(this); start0(); 一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true,事实中不管这个线程后来有没有执行到底,只要调用了一次start()就再也没有机会运行了,这意味着:通过Thread实例的start(),一个Thread的实例只能产生一个线程那么如果要在一个实例上产生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能。class R implements Runnable private int

18、x = 0; public void run() for(int i=0;i100;i+) try Thread.sleep(10); catch(Exception e) System.out.println(x+); 正如它的名字一样,Runnable的实例是可运行的,但它自己并不能直接运行,它需要被Thread对象来包装才行运行:public class Test public static void main(String args) throws Exception new Thread(new R().start(); 当然这个结果和mt.start()没有什么区别。但如果我们把一

19、个Runnable实例给Thread对象多次包装,我们就可以看到它们实际是在同一实例上启动线程:public class Test public static void main(String args) throws Exception R r = new R(); for(int i=0;i10;i+) new Thread(r).start(); x是实例对象,但结果是x被加到了999,说明这10个线程是在同一个r对象上运行的。请大家注意,因为这个例子是在单CPU上运行的,所以没有对多个线程同时操作共同的对象进行同步。这里是为了说明的方便而简化了同步,而真正的环境中你无法预知程序会在什么

20、环境下运行,所以一定要考虑同步。到这里我们做一个完整的例子来说明线程产生的方式不同而生成的线程的区别:package debug;import java.io.*;import java.lang.Thread;class MyThread extends Thread public int x = 0; public void run() System.out.println(+x); class R implements Runnable private int x = 0; public void run() System.out.println(+x); public class Te

21、st public static void main(String args) throws Exception for(int i=0;i10;i+) Thread t = new MyThread(); t.start(); Thread.sleep(10000);/让上面的线程运行完成 R r = new R(); for(int i=0;i10;i+) Thread t = new Thread(r); t.start(); 上面10个线程对象产生的10个线程运行时打印了10次1。下面10个线程对象产生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnabl

22、e实例)的多个线程。下节我们将研究线程对象方法,还是那句话,一般文档中可以读到的内容我不会介绍太多请大家自己了解。Java多线程编程基础之非线程的方法 wait(),notify()/notityAll()方法关于这两个方法,有很多的内容需要说明.在下面的说明中可能会有很多地方 不能一下子明白,但在看完本节后,即使不能完全明白,你也一定要回过头来记住 下面的两句话:wait(),notify()/notityAll()方法是普通对象的方法(Object超类中实现), 而不是线程对象的方法wait(),notify()/notityAll()方法只能在同步方法中调用线程的互斥控制多个线程同时操作

23、某一对象时,一个线程对该对象的操作可能会改变其状态, 而该状态会影响另一线程对该对象的真正结果.这个例子我们在太多的文档中可以看到,就象两个操售票员同时售出同一张票 一样.1.线程A在数据库中查询存票,发现票C可以卖出2.线程A接受用户订票请求,准备出票 .3.这时切换到了线程B执行4.线程B在数据库中查询存票,发现票C可以卖出5.线程B将票卖了出去6.切换到线程A执行,线程A卖了一张已经卖出的票所以需要一种机制来管理这类问题的发生,当某个线程正在执行一个不可分割 的部分时,其它线程不能不能同时执行这一部分.象这种控制某一时刻只能有一个线程执行某个执行单元的机制就叫互斥控制 或共享互斥(mut

24、ual exclusion)在JAVA中,用synchornized关键字来实现互斥控制(暂时这样认为,JDK1.5已经 发展了新的机制)synchornized关键字把一个单元声明为synchornized,就可以让在同一时间只有一个线程操作该方 法.有人说synchornized就是一把锁,事实上它确实存在锁,但是是谁的锁,锁谁, 这是一个非常复杂的问题.每个对象只有一把监视锁(monitor lock),一次只能被一个线程获取.当一个 线程获取了这一个锁后,其它线程就只能等待这个线程释放锁才能再获取.那么synchornized关键字到底锁什么?得到了谁的锁?对于同步块,synchorn

25、ized获取的是参数中的对象锁:synchornized(obj)/.线程执行到这里时,首先要获取obj这个实例的锁,如果没有获取到线程只能等 待.如果多个线程执行到这里,只能有一个线程获取obj的锁,然后执行中的语句 ,所以,obj对象的作用范围不同,控制程序不同.假如:public void test()Object o = new Object();synchornized(obj)/.这段程序控制不了任何,多个线程之间执行到Object o = new Object();时会 各自产生一个对象然后获取这个对象有监视锁,各自皆大欢喜地执行.而如果是类的属性:class TestObjec

26、t o = new Object();public void test()synchornized(o)/.所有执行到Test实例的synchornized(o)的线程,只有一个线程可以获取到监 视锁.有时我们会这样:public void test()synchornized(this)/.那么所有执行Test实例的线程只能有一个线程执行.而synchornized(o)和 synchornized(this)的范围是不同的,因为执行到Test实例的synchornized(o)的 线程等待时,其它线程可以执行Test实例的synchornized(o1)部分,但多个线程同 时只有一个可以执

27、行Test实例的synchornized(this).而对于synchornized(Test.class)/.这样的同步块而言,所有调用Test多个实例的线程赐教只能有一个线程可以执 行.synchornized方法如果一个方法声明为synchornized的,则等同于把在为个方法上调用 synchornized(this).如果一个静态方法被声明为synchornized,则等同于把在为个方法上调用 synchornized(类.class).现在进入wait方法和notify/notifyAll方法.这两个(或叫三个)方法都是 Object对象的方法,而不是线程对象的方法.如同锁一样,它

28、们是在线程中调用某 一对象上执行的.class Testpublic synchornized void test()/获取条件,int x 要求大于100;if(x 100)wait();这里为了说明方法没有加在trycatch()中,如果没有明确在哪个对象上调 用wait()方法,则为this.wait();假如:Test t = new Test();现在有两个线程都执行到t.test();方法.其中线程A获取了t的对象锁,进入 test()方法内.这时x小于100,所以线程A进入等待.当一个线程调用了wait方法后,这个线程就进入了这个对象的休息室 (waitset),这是一个虚拟的对

29、象,但JVM中一定存在这样的一个数据结构用来记录 当前对象中有哪些程线程在等待.当一个线程进入等待时,它就会释放锁,让其它线程来获取这个锁.所以线程B有机会获得了线程A释放的锁,进入test()方法,如果这时x还是小于 100,线程B也进入了t的休息室.这两个线程只能等待其它线程调用notityAll来唤醒.但是如果调用的是有参数的wait(time)方法,则线程A,B都会在休息室中等待 这个时间后自动唤醒.为什么真正的应用都是用while(条件)而不用if(条件)在实际的编程中我们看到大量的例子都是用?while(x 100)wait();go();而不是用if,为什么呢?在多个线程同时执行时,if(x 100)是不安全的.因为如果线程A和线程B都 在t的休息室中等待,这时另一个线程使x=100了,并调用notifyAll方法,线程A继 续执行下面的go().而它执行完成后,x有可能又小于100,比如下面的程序中调用 了-x,这时切换到线程B,线程B没有继续判断,直接执行go();就产生一个错误的 条件,只有while才能保证线程B又继续检查一次。notify/notifyAll方法这两个方法都是把某个对象上休息区内的线程唤醒,notify只能唤醒一个

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1