jave培训:多线程培训资料课件_第1页
jave培训:多线程培训资料课件_第2页
jave培训:多线程培训资料课件_第3页
jave培训:多线程培训资料课件_第4页
jave培训:多线程培训资料课件_第5页
已阅读5页,还剩56页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

第七讲多线程多线程基本概念创建线程的方式线程的生命周期及控制线程的优先级及调度

多线程的互斥与同步守护线程(Daemon)线程组(ThreadGroup)第一页,共六十一页。并发现象在现实生活中大量存在人体(消化、运动)计算机(同时运行多中程序)多线程——在一个程序中实现并发编程语言一般提供了串行程序设计的方法计算机的并发能力由操作系统提供Java在语言级提供多线程并发的概念1、多线程基本概念第二页,共六十一页。1、多线程基本概念以前所编写的程序,每个程序都有一个入口、一个出口以及一个顺序执行的序列,在程序执行过程中的任何指定时刻,都只有一个单独的执行点。事实上,在单个程序内部是可以在同一时刻进行多种运算的,这就是所谓的多线程(这与多任务的概念有相似之处)。一个单独的线程和顺序程序相似,也有一个入口、一个出口以及一个顺序执行的序列,从概念上说,一个线程是一个程序内部的一个顺序控制流。线程并不是程序,它自己本身并不能运行,必须在程序中运行。在一个程序中可以实现多个线程,这些线程同时运行,完成不同的功能。将军令多线程第三页,共六十一页。1、多线程基本概念第四页,共六十一页。从逻辑的观点来看,多线程意味着一个程序的多行语句同时执行,但是多线程并不等于多次启动一个程序,操作系统也不会把每个线程当作独立的进程来对待:两者的粒度不同,是两个不同层次上的概念。进程是由操作系统来管理的,而线程则是在一个程序(进程)内。不同进程的代码、内部数据和状态都是完全独立的,而一个程序内的多线程是共享同一块内存空间和同一组系统资源,有可能互相影响。线程本身的数据通常只有寄存器数据,以及一个程序执行时使用的堆栈,所以线程的切换比进程切换的负担要小。1、多线程基本概念第五页,共六十一页。进程:程序的一次执行。程序代码程序数据程序资源线程:进程中程序代码的一个执行序列。程序调用堆栈线程局部变量可共享访问进程中的数据和资源操作系统按线程来调度程序的执行1、多线程基本概念第六页,共六十一页。文件输入输出装置各种系统资源数据区段程序区段只有一个地方在执行文件输入输出装置各种系统资源数据区段程序区段同时有数个地方在执行传统的进程多线程的任务1、多线程基本概念第七页,共六十一页。1、多线程基本概念多线程的优势:多线程编程简单,效率高(能直接共享数据和资源,多进程不能)适合于开发服务程序(如Web服务,聊天服务等)适合于开发有多种交互接口的程序(如聊天程序的客户端,网络下载工具)适合于有人机交互又有计算量的程序(如字处理程序Word,Excel)减轻编写交互频繁、涉及面多的程序的困难(如监听网络端口)程序的吞吐量会得到改善(同时监听多种设备,如网络端口、串口、并口以及其他外设)有多个处理器的系统,可以并发运行不同的线程(否则,任何时刻只有一个线程在运行)第八页,共六十一页。1、多线程基本概念虽然各种操作系统(Unix/Linux、Windows系列等)都支持多线程,但若要用C、C++或其他语言编写多线程程序是十分困难的,因为它们对数据同步的支持不充分。对多线程的综合支持是Java语言的一个重要特色,它提供了Thread类来实现多线程。在Java中,线程可以认为是由三部分组成的:虚拟CPU,封装在java.lang.Thread类中,它控制着整个线程的运行;执行的代码,传递给Thread类,由Thread类控制顺序执行;处理的数据,传递给Thread类,是在代码执行过程中所要处理的数据。第九页,共六十一页。2、创建线程的方式Java的线程是通过Java的软件包java.lang中定义的类Thread来实现的。当生成一个Thread类的对象之后,就产生了一个线程,通过该对象实例,可以启动线程、终止线程、或者暂时挂起线程等。第十页,共六十一页。2、创建线程的方式Thread类本身只是线程的虚拟CPU;线程所执行的代码(或者说线程所要完成的功能)是通过方法run()(包含在一个特定的对象中)来完成的,方法run()称为线程体。实现线程体的特定对象是在初始化线程时传递给线程的。在一个线程被建立并初始化以后,Java的运行时系统就自动调用run()方法,正是通过run()方法才使得建立线程的目的得以实现。通常,run()方法是一个循环,例如一个播放动画的线程要循环显示一系列图片。有时,run()方法会执行一个时间较长的操作,例如下载并播放一个JPEG格式的电影。第十一页,共六十一页。先来看看线程对象的初始化,类Thread的构造方法如下:publicThread(ThreadGroupgroup,Runnabletarget,Stringname)

group指明了线程所属的线程组;target是线程体run()方法所在的对象;name是线程的名称。target必须实现接口Runnable。在接口Runnable中只定义了一个方法voidrun()作为线程体。任何实现接口Runnable的对象都可以作为一个线程的目标对象。类Thread本身也实现了接口Runnable(空实现),因此,上述构造方法中各参数都可以为null。2、创建线程的方式第十二页,共六十一页。从Thread类的构造方法可以看出,用户可以有两种方法构造自己的run()方法。方法一:

定义一个线程类,它继承类Thread并重写其中的方法run()。这时在初始化这个类的实例时,目标对象target可以为null,表示这个实例本身具有线程体。由于Java只支持单继承,用这种方法定义的类不能再继承其他类。2、创建线程的方式第十三页,共六十一页。classSimpleThreadextendsThread{publicSimpleThread(Stringstr){super(str);}

publicvoidrun(){for(inti=0;i<10;i++){System.out.println(i+""+getName());try{sleep((int)(Math.random()*1000));}catch(InterruptedExceptione){}}System.out.println("DONE!"+getName());}}TwoThreadsTest.java2、创建线程的方式第十四页,共六十一页。方法二:

提供一个实现接口Runnable的类作为线程的目标对象。在初始化一个Thread类或子类生成线程实例时,把目标对象传递给这个线程实例,由该目标对象提供线程体run()方法。这时,实现接口Runnable的类还可以再继承其他类,即实现接口Runnable的类可以不单纯是提供线程体。2、创建线程的方式第十五页,共六十一页。publicclassClockextendsjava.applet.AppletimplementsRunnable{ThreadclockThread;publicvoidstart(){if(clockThread==null){ clockThread=newThread(this,"Clock"); clockThread.start();}}

publicvoidrun(){while(clockThread!=null){ repaint(); try{ clockThread.sleep(1000); }catch(InterruptedExceptione){ }}}publicvoidpaint(Graphicsg){Datenow=newDate();g.drawString(now.getHours()+":"+now.getMinutes()+":"+now.getSeconds(),5,10);}publicvoidstop(){clockThread.stop();clockThread=null;}}Clock.java第十六页,共六十一页。run()方法是运行线程的主体,启动线程时,由java直接调用publicvoidrun()。使用Runnable接口可以将线程的虚拟CPU、代码和数据分开,形成一个比较清晰的模型。使用Runnable接口使得包含线程体的类还可以继承其他类。直接继承Thread类以后不能再继承其他类,但编写简单,并可直接操纵线程;使用Runnable接口时,若要在run()方法中操纵线程,必须使用Thread.currentThread()方法。在具体应用中,采用哪种方法来构造线程体要根据具体情况而定。通常,当一个线程体所在的类已经继承了另一个类时,就应该用实现Runnable接口的方法。2、创建线程的方式第十七页,共六十一页。2、创建线程的方式Thread类的方法voidrun()线程所执行的代码voidstart()throwsIllegalThreadStateException使程序开始执行,多次调用会产生例外voidsleep(longmilis)让线程睡眠一段时间,此期间线程不消耗CPU资源voidinterrupt()中断线程staticbooleaninterrupted()判断当前线程是否被中断(会清除中断状态标记)booleanisInterrupted()判断指定线程是否被中断线程状态控制方法第十八页,共六十一页。2、创建线程的方式Thread类的方法booleanisAlive()判断线程是否处于活动状态(即已调用start,但run还未返回)staticThreadcurrentThread()返回当前线程对象的引用voidsetName(StringthreadName)改变线程的名字StringgetName()获得线程的名字StringtoString()例子:Thread[Thread-0,1,test]voidjoin([long

millis[,int

nanos]])等待线程结束第十九页,共六十一页。2、创建线程的方式Thread类的方法voiddestroy()销毁线程voidstop()终止线程的执行voidsuspend()/voidresume()挂起线程/staticvoidyield()暂停当前线程,让其他线程执行voidsetDaemon(booleanon)/voidsetPriority(intp)获得线程的名字notify()/notifyAll()/wait()-从Object继承而来第二十页,共六十一页。3、线程的生命周期及控制线程是程序内部的一个顺序控制流,它具有一个特定的生命周期。在一个线程的生命周期中,它总处于某一种状态中。线程的状态表示了线程正在进行的活动以及在这段时间内线程能完成的任务。第二十一页,共六十一页。3、线程的生命周期及控制newThread()NewThreadRunnablestart()NotRunnablestop()stop()Deadyield()stop()run()exit..suspend()sleep()wait()I/O流阻塞resume()notify()/notifyAll()I/O指令.waitingsleepingsuspendingblockedreadyrunning第二十二页,共六十一页。bornreadyrunningwaitingsleepingdeadblockedstart()dispatchquantumexpirationyield()waitsleep()runcompleteissueI/Orequestwaitintervalexpiresnotify()notifyAll()interrupt()I/Ocompletesleepintervalexpiresinterrupt()3、线程的生命周期及控制第二十三页,共六十一页。3、线程的生命周期及控制创建状态(newThread)当创建了一个新的线程时(myThreadthd=newmyThread();),它就处于创建状态,此时它仅仅是一个空的线程对象,系统不为它分配资源。处于这种状态时只能启动或终止该线程,调用除这两种以外的其它线程状态相关的方法都会失败并且会引起非法状态例外IllegalThreadStateException(对于其它状态,若所调用的方法与状态不符,都会引起非法状态例外)。第二十四页,共六十一页。可运行状态(Runnable)当线程处于创建状态时,可以调用start()方法(thd.start();)来启动它,产生运行这个线程所需的系统资源,安排其运行,并调用线程体run()方法,这样就使得该线程处于可运行(Runnable)状态。需要注意的是这一状态并不是运行中状态(Running),因为线程也许实际上并未真正运行(Ready)。由于很多计算机都是单处理器的,所以要在同一时刻运行所有的处于可运行状态的线程是不可能的,Java运行系统必须实现调度来保证这些线程共享处理器。但是在大多数情况下,可运行状态也就是运行中。当一个线程正在运行时,它是可运行的,并且也是当前正运行的线程。3、线程的生命周期及控制第二十五页,共六十一页。3、线程的生命周期及控制

不可运行状态(NotRunnable)线程处于可运行状态时,当下面四种情况发生,线程就进入不可运行状态:调用了sleep()方法;调用了suspend()方法;为等候一个条件变量,线程调用wait()方法;输入输出流中发生线程阻塞。ThreadmyThread=newMyThreadClass();myThread.start();try{myThread.sleep(10000);}catch(InterruptedExceptione){}第二十六页,共六十一页。

不可运行状态(NotRunnable)对于这四种使得线程处于不可运行状态的情况,都有特定的方法使线程返回可运行状态:

如果线程处于睡眠状态中,sleep()方法中的参数为休息时间,当这个时间过去后,线程即为可运行的;如果一个线程被挂起,须调用resume()方法来返回;如果线程在等待条件变量,那么要停止等待的话,需要该条件变量所在的对象调用notify()或notifyAll()方法;如果在I/O流中发生线程阻塞,则特定的I/O指令将结束这种不可运行状态。需要注意的是每种方法都仅仅对相应的情况才有作用,例如当一个线程睡眠并且睡眠时间还没有结束时,调用resume()方法是无效的,并且还会引起非法状态例外。3、线程的生命周期及控制第二十七页,共六十一页。

死亡状态(Dead)线程的终止一般可通过两种方法实现:自然撤消或是被停止。自然撤消是指从线程的run()方法正常退出;而调用线程的实例方法stop()则可以强制停止当前线程。3、线程的生命周期及控制//线程自然撤销publicvoidrun(){inti=0;while(i<100){i++;System.out.println("i="+i);}}//线程被强行停止myThreadthd=newMyThread();thd.start();try{thd.sleep(10000);}catch(InterruptedExceptione){}thd.stop();第二十八页,共六十一页。3、线程的生命周期及控制可以通过在其他线程中调用stop()方法来终止线程,也可以由线程自己调用stop()方法,自我终止。如果希望线程正常终止,可采用标记来使线程中的run()方法退出。//线程自我终止publicvoidrun(){while(true){…//完成线程的特定功能if(time_to_die){Thread.currentThread().stop();}}}第二十九页,共六十一页。publicclassXyzimplementsRunnable{privatebooleantimeToQuit=false;publicvoidrun(){while(!timeToQuit){…}}publicvoidstopRunning(){timeToQuit=true;}}3、线程的生命周期及控制publicclassControlThread{privateXyzr=newXyz();privateThreadt=newThread(r);publicvoidstartThread(){t.start();}publivoidstopThread(){r.stopRunning();}}第三十页,共六十一页。3、线程的生命周期及控制isAlive()方法:可以用来判断线程目前是否正在执行中。如果线程已被启动并且未被终止,那么isAlive()返回true,但该线程是可运行或是不可运行的,是不能作进一步的分辨。如果返回false,则该线程是新创建或是已被终止的(同样不能作进一步的分辨)。join()方法:等待线程执行完毕。yield()方法:将执行的权力交给其它优先级相同的线程,当前执行线程到可运行线程队列(ready)的最后等待,若队列空,该方法无效。myThreadthd=newMyThread();thd.start();…thd.join();//等待线程thd执行完后再继续往下执行…join(inttime):最多等待time所指定的时间。第三十一页,共六十一页。sleep()方法可以暂停线程的执行,让其它线程得到机会。但sleep()要丢出例外InterruptedException,必须抓住。try{sleep(100)}catch(InterruptedExceptione){…//本例外可不作处理}suspend()方法和resume()可以用来暂停线程或恢复线程。可以由线程自身在线程体中调用suspend()方法暂停自己,也可以在其他线程中通过线程实例调用suspend()方法暂停线程的执行。但是要恢复由suspend()方法暂停的线程,只能在其他线程中通过线程实例调用resume()方法。3、线程的生命周期及控制第三十二页,共六十一页。Java提供一个线程调度器来监控程序中启动后进入可运行状态的所有线程。线程调度器按照线程的优先级决定调度哪些线程来执行,具有高优先级的线程会在较低优先级的线程之前得到执行。同时线程的调度是抢先式的,即如果当前线程在执行过程中,一个具有更高优先级的线程进入可执行状态,则该告优先级的线程会被立即调度执行。多个线程运行时,若线程的优先级相同,由操作系统按时间片轮转方式或独占方式来分配线程的执行时间。4、线程的优先级及调度第三十三页,共六十一页。在Java中线程的优先级是用数字来表示的,分为三个级别:低优先级:Thread.MIN_PRIORITY,数值为1(2~4)缺省优先级:Thread.NORM_PRIORITY,数值为5高优先级:Thread.MAX_PRIORITY,数值为10(6~9)具有相同优先级的多个线程,若它们都为高优先级Thread.MAX_PRIORITY,则每个线程都是独占式的,也就是说这些线程将被顺序执行;若该优先级不为高优先级,则这些线程将同时执行,也就是说这些线程的执行是无序的。线程被创建后,其缺省的优先级是缺省优先级Thread.NORM_PRIORITY。可以用方法intgetPriority()来获得线程的优先级,同时也可以用方法voidsetPriority(intp)在线程被创建后改变线程的优先级。4、线程的优先级及调度第三十四页,共六十一页。4、线程的优先级及调度线程离开执行状态, 如果:线程结束线程处于I/O等待线程调用sleep线程调用wait线程调用join线程调用yield有更高优先级的线程时间片用完Priority10Priority9Priority8Priority7Priority6Priority5Priority4Priority3Priority2Priority1ABCDEFGHIJKThreadPriority.java第三十五页,共六十一页。1.用管道流实现线程间的通信线程1PipedOutputStreamPipedInputStream输出流outStream输入流inStream线程22.通过一个中间类在线程间传递信息线程2线程1中间类mssm.write(s)s=m.read()write()read()5、多线程的互斥与同步——线程间的通信第三十六页,共六十一页。管道流可以连接两个线程间的通信。下面的例子里有两个线程在运行,一个写线程往管道流中输出信息,一个读线程从管道流中读入信息。5、多线程的互斥与同步——线程间的通信主类Pipethread辅类myWriter线程类辅类myReader线程类管道流将数据写到输出流从输入流中读数据输出流作为参数传给myWriter输入流作为参数传给myReader第三十七页,共六十一页。classmyWriterextendsThread{privatePipedOutputStreamoutStream;//将数据输出privateStringmessages[]={"Monday","Tuesday","Wednsday","Thursday","Friday","Saturday","Sunday"};publicmyWriter(PipedOutputStreamo)

{outStream=o;}publicvoidrun()

{PrintStreamp=newPrintStream(outStream);for(inti=0;i<messages.length;i++){p.println(messages[i]);p.flush();System.out.println("Write:"+messages[i]);}p.close();p=null;}}第三十八页,共六十一页。classmyReaderextendsThread{privatePipedInputStreaminStream;//从中读数据

publicmyReader(PipedInputStreami){inStream=i;}publicvoidrun(){Stringline;DataInputStreamd;booleanreading=true;d=newDataInputStream(inStream);while(reading&&d!=null){try{line=d.readLine();if(line!=null)System.out.println(”Read:"+line);elsereading=false;}catch(IOExceptione){}}try{Thread.currentThread().sleep(4000);}catch(InterruptedExceptione){}}}第三十九页,共六十一页。publicclassPipethread{

publicstaticvoidmain(Stringargs[]){PipethreadthisPipe=newPipethread();thisPcess();}publicvoidprocess(){PipedInputStreaminStream;PipedOutputStreamoutStream;try{

outStream=newPipedOutputStream();inStream=newPipedInputStream(outStream);newmyWriter(outStream).start();newmyReader(inStream).start();}catch(IOExceptione){}}}Pipethread.java第四十页,共六十一页。5、多线程的互斥与同步——线程间的资源互斥共享通常,一些同时运行的线程需要共享数据。在这种时候,每个线程就必须要考虑其他与他一起共享数据的线程的状态与行为,否则的话就不能保证共享数据的一致性,从而也就不能保证程序的正确性。classStack{intindex=0;char[]data=newchar[6];publicvoidpush(charc){data[index]=c;index++;}publiccharpop(){index--;returndata[index];}}第四十一页,共六十一页。当有两个线程A和B同时使用了Stack类的一个实例时,在某一时刻,A要往堆栈里push数据,而B则要从堆栈中pop数据:(1)操作之前,堆栈中有两个字符:data=|a|c|||||index=2(2)A执行push中的第一条语句data[index]=‘r’:data=|a|c|r||||index=2(3)A还没有执行index++语句,A被B中断,B执行pop()方法,返回‘c’:data=|a|c|r||||index=1(4)A继续执行index++语句:data=|a|c|r||||index=2最后的结果是r并没有添加到堆栈中去。5、多线程的互斥与同步——线程间的资源互斥共享第四十二页,共六十一页。线程1线程2线程10资源取过来加1后送回去取钱1取钱2透支余额变量classbank{

staticdoublebalance;publicboolean取钱(doubleamount){if(balance>=amount){balance-=amount;returntrue;}elsereturnfalse;}}5、多线程的互斥与同步——线程间的资源互斥共享第四十三页,共六十一页。产生这种问题的原因是对共享资源访问的不完整。为了解决这种问题,需要寻找一种机制来保证对共享数据操作的完整性,这种完整性称为共享数据操作的同步,共享数据叫做条件变量。在Java语言中,引入了“对象互斥锁”的概念(又称为监视器、管程)来实现不同线程对共享数据操作的同步。“对象互斥锁”阻止多个线程同时访问同一个条件变量。在Java语言中,有两种方法可以实现“对象互斥锁”:用关键字volatile来声明一个共享数据(变量);用关键字synchronized来声明一个操作共享数据的方法或一段代码。5、多线程的互斥与同步——线程间的资源互斥共享第四十四页,共六十一页。classstack{intindex=0;char[]data=newchar[6];publicsynchronizedvoidpush(charc){data[index]=c;index++;}publicsynchronizedcharpop(){index--;returndata[index];}}classbank{

staticvolatiledoublebalance;public[syn…]boolean取钱(doubleamt){if(balance>=amt){balance-=amt;returntrue;}elsereturnfalse;}}classstack{…publicvoidpush(charc){

synchronized(this){data[index]=c;index++;

}}publiccharpop(){…}}5、多线程的互斥与同步——线程间的资源互斥共享第四十五页,共六十一页。用synchronized来标识的代码段或方法即为“对象互斥锁”锁住的部分。如果一个程序内有两个或以上的方法使用synchronized标志,则它们在同一个“对象互斥锁”管理之下。一般情况下,都使用synchronized关键字在方法的层次上实现对共享资源操作的同步,很少使用volatile关键字声明共享变量。pushpop互斥锁线程1线程25、多线程的互斥与同步——线程间的资源互斥共享第四十六页,共六十一页。除了要处理多线程间共享数据操作的同步问题之外,在进行多线程程序设计时,还会遇到另一类问题,这就是如何控制相互交互的线程之间的运行进度,即多线程的同步。典型的模型:生产者——消费者问题5、多线程的互斥与同步——线程间的同步生产者消费者共享对象putget若共享对象中只能存放一个数据,可能出现以下问题:生产者比消费者快时,消费者会漏掉一些数据没有取到;消费者比生产者快时,消费者取相同的数据。第四十七页,共六十一页。为了解决所出现的问题,在Java语言中可以用wait()和notify()/notifyAll()方法(在java.lang.Object类中定义)来协调线程间的运行进度(读取)关系。wait()方法的作用是让当前线程释放其所持有的“对象互斥锁”,进入wait队列(等待队列);而notify()/notifyAll()方法的作用是唤醒一个或所有正在等待队列中等待的线程,并将它(们)移入等待同一个“对象互斥锁”的队列。

需要指出的是:notify()/notifyAll()方法和wait()方法都只能在被声明为synchronized的方法或代码段中调用。5、多线程的互斥与同步——线程间的同步第四十八页,共六十一页。publicclassProducerConsumerTest{publicstaticvoidmain(Stringargs[]){

CubbyHolec=newCubbyHole();//共享资源Producerp1=newProducer(c,1);//资源生产者Consumerc1=newConsumer(c,1);//资源消耗者p1.setPriority(Thread.MAX_PRIORITY);c1.setPriority(Thread.MAX_PRIORITY);p1.start();c1.start();}}第四十九页,共六十一页。classProducerextendsThread{privateCubbyHolecubbyhole;privateintnumber;publicProducer(CubbyHolec,intnumber){cubbyhole=c;this.number=number;}publicvoidrun(){for(inti=0;i<10;i++){

cubbyhole.put(i);System.out.println("Producer#"+this.number+"put:"+i);try{

sleep((int)(Math.random()*100));}catch(InterruptedExceptione){}}}}第五十页,共六十一页。classConsumerextendsThread{privateCubbyHolecubbyhole;privateintnumber;publicConsumer(CubbyHolec,intnumber){cubbyhole=c;this.number=number;}publicvoidrun(){intvalue=0;for(inti=0;i<10;i++){

value=cubbyhole.get();System.out.println("Consumer#"+this.number+"got:"+value);}}}第五十一页,共六十一页。classCubbyHole{privateintseq;privatebooleanavailable=false;//信号量publicsynchronizedintget(){

while(available==false){try{wait();//waitsfornotify()callfromProducer}catch(InterruptedExceptione){}}available=false;

notify();returnseq;}

}publicsynchronizedvoidput(intvalue){

while(available==true){try{wait(); //waitsfornotify()callfromconsumer}catch(InterruptedExceptione){}}seq=value;available=true;

notify();}…get()方法在读信息之前先等待,直到信息可读,读完后通知要写的线程。put()方法在写信息之前先等待,直到信息被取走,写完后通知要读的进程。ProducerConsumerTest.java第五十二页,共六十一页。如果一个线程持有一个锁并试图获取另一个锁

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论