Java线程及多线程技术及应用.doc_第1页
Java线程及多线程技术及应用.doc_第2页
Java线程及多线程技术及应用.doc_第3页
Java线程及多线程技术及应用.doc_第4页
Java线程及多线程技术及应用.doc_第5页
已阅读5页,还剩21页未读 继续免费阅读

下载本文档

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

文档简介

第6 章 Java线程及多线程技术及应用6.1线程基本概念1、进程和线程的基础知识l 进程:运行中的应用程序称为进程,拥有系统资源(cpu、内存)l 线程:进程中的一段代码,一个进程中可以哦有多段代码。本身不拥有资源(共享所在进程的资源)在java中,程序入口被自动创建为主线程,在主线程中可以创建多个子线程。区别: 1、是否占有资源问题 2、创建或撤销一个进程所需要的开销比创建或撤销一个线程所需要的开销大。 3、进程为重量级组件,线程为轻量级组件l 多进程: 在操作系统中能同时运行多个任务(程序)l 多线程: 在同一应用程序中有多个功能流同时执行 2、线程的主要特点l 不能以一个文件名的方式独立存在在磁盘中;l 不能单独执行,只有在进程启动后才可启动;l 线程可以共享进程相同的内存(代码与数据)。3、线程的主要用途l 利用它可以完成重复性的工作(如实现动画、声音等的播放)。l 从事一次性较费时的初始化工作(如网络连接、声音数据文件的加载)。l 并发执行的运行效果(一个进程多个线程)以实现更复杂的功能4、多线程(多个线程同时运行)程序的主要优点l 可以减轻系统性能方面的瓶颈,因为可以并行操作;l 提高CPU的处理器的效率,在多线程中,通过优先级管理,可以使重要的程序优先操作,提高了任务管理的灵活性;另一方面,在多CPU系统中,可以把不同的线程在不同的CPU中执行,真正做到同时处理多任务。6.2 线程创建与启动1、与线程编程有关的一些概念 创建方式: 1 继承java.lang.Thread类 2 实现java.lang.Runnable接口线程体:public void run()方法,其内的程序代码决定了线程的行为和功能。线程启动: public void start () , 线程启动后,需要获取cpu才能自动调用run()运行。线程休眠: public void sleep(long ms), 线程将暂停,放弃cpu2、利用继承Thread类创建线程的示例package com.px1987.j2se.thread.base;/*通过Thread类实现多线程 定义一个Thread的子类并重写其run方法.*/public class MyThread extends Thread Overridepublic void run() while (true) System.out.println(invoke MyThread run method);public static void main(String args) / main方法测试线程的创建与启动MyThread myThread = new MyThread();/ 实例化MyThread的对象myThread.start();/ 调用myThread对象的start方法启动一个线程3、利用实现Runable接口创建线程的示例package com.px1987.j2se.thread.base;/*通过Runable接口实现多线程 定义MyRunable类实现Runnable接口,并实现接口中的run方法。*/public class MyRunable implements Runnable public void run() while (true)System.out.println(invoke MyRunable run method);public static void main(String args) / main方法测试线程的创建与启动/ 建立MyRunable类的对象,以此对象为参数建立Thread类的对象Thread thread = new Thread(new MyRunable();thread.start();/ 调用thread对象的start方法启动一个线程6.3 线程的状态控制1、新建状态用new关键字和Thread类或其子类建立一个线程对象后,该线程对象就处于新生状态。处于新生状态的线程有自己的内存空间,通过调用start方法进入就绪状态(runnable)。2、就绪状态处于就绪状态的线程已经具备了运行条件,但还没有分配到CPU,处于线程就绪队列,等待系统为其分配CPU。等待状态并不是执行状态,当系统选定一个等待执行的Thread对象后,它就会从等待执行状态进入执行状态,系统挑选的动作称之为“cpu调度”。一旦获得CPU,线程就进入运行状态并自动调用自己的run方法。3、死亡状态死亡状态是线程生命周期中的最后一个阶段。线程死亡的原因有两个:一个是正常运行的线程完成了它的全部工作;另一个是线程被强制性地终止,如通过执行stop或destroy方法来终止一个线程。 Method stop() & destroy() in the class Thread is deprecated。 当一个线程进入死亡状态以后,就不能再回到其它状态了。 让一个Thread对象重新执行一次的唯一方法,就是重新产生一个Thread对象。4、体现线程状态转变的代码示例package com.px1987.j2se.thread.base;public class MyRunable1 implements Runnable public void run() while (true)System.out.println(invoke MyRunable run method);public static void main(String args) Thread thread = new Thread(new MyRunable();/ 新生状态thread.start();/ 就绪状态,获得CPU后就能运行try Thread.sleep(5000); catch (InterruptedException e) e.printStackTrace();thread.stop();/ 死亡状态通过查API可以看到stop方法和destory方法已经过时了,所以不能再用,那要怎样做才能强制的销毁一个线程呢?1、在run方法中执行return 线程同样结束2、可以在while循环的条件中设定一个标志位,当它等于false的时候,while循环就不在运行,这样线程也就结束了。代码为实现的代码示例:package com.px1987.j2se.thread.StateControl;public class MyRunable2 implements Runnable private boolean isStop;/线程是否停止的标志位public void run() while (!isStop)System.out.println(invoke MyRunable run method);public void stop()/终止线程isStop=true;public static void main(String args) MyRunable myRunable=new MyRunable();Thread thread = new Thread(myRunable);thread.start();try Thread.sleep(5000);catch (InterruptedException e) e.printStackTrace();myRunable.stop();/正确的停止线程的方法5、阻塞状态处于运行状态的线程在某些情况下,如执行了sleep(睡眠)方法,或等待I/O设备等资源,将让出CPU并暂时停止自己的运行,进入阻塞状态。 在阻塞状态的线程不能进入就绪队列。只有当引起阻塞的原因消除时,如睡眠时间已到,或等待的I/O设备空闲下来,线程便转入就绪状态,重新到就绪队列中排队等待,被系统选中后从原来停止的位置开始继续运行。有三种方法可以暂停Threads执行:(1)sleep方法可以调用Thread的静态方法:public static void sleep(long millis) throws InterruptedException 使得当前线程休眠(暂时停止执行millis毫秒)。由于是静态方法,sleep可以由类名直接调用:Thread.sleep()。下面为代码示例:package com.px1987.j2se.thread.p5;import java.util.Date;import java.text.SimpleDateFormat;class SleepTest implements Runnable private static SimpleDateFormat format = new SimpleDateFormat(yyyy-MM-dd hh:mm:ss);public void run() System.out.println(child thread begin);int i = 0;while (i+ 5) System.out.println(format.format(new Date();try Thread.sleep(5000);catch (InterruptedException e) e.printStackTrace();System.out.println(child thread dead at: + format.format(new Date();public static void main(String args) Runnable r = new SleepTest();Thread thread = new Thread(r);thread.start();try Thread.sleep(20000); catch (InterruptedException e) e.printStackTrace();errupt();System.out.println(main method dead!);该程序的运行结果如下:child thread begin2009-02-06 04:50:292009-02-06 04:50:342009-02-06 04:50:392009-02-06 04:50:44main method dead!java.lang.InterruptedException: sleep interruptedat java.lang.Thread.sleep(Native Method)at com.px1987.j2se.thread.p5.Thread4.run(Thread4.java:17)at java.lang.Thread.run(Unknown Source)2009-02-06 04:50:49child thread dead at: 2009-02-06 04:50:54(2)yield方法让出CPU的使用权,从运行态直接进入就绪态。下面为代码示例:package com.px1987.j2se.thread.StateControl;class Thread5 implements Runnable private String name;Thread5(String s) = s;public void run() for (int i = 1; i = 50; i+) System.out.println(name + : + i);if (i % 10 = 0) Thread.yield();package com.px1987.j2se.thread.StateControl;public class YieldTest public static void main(String args) Runnable r1 = new Thread5(S1);Runnable r2 = new Thread5(S2);Thread t1 = new Thread(r1);Thread t2 = new Thread(r2);t1.start();t2.start();try Thread.sleep(2);catch (InterruptedException e) e.printStackTrace(); System.out.println(main method over!);该程序的部分运行结果如下:S1: 20S2: 7S2: 8S2: 9S2: 10S1: 41S1: 42S1: 43S1: 44S1: 45S1: 46S1: 47S1: 48S1: 49S1: 50S2: 11S2: 12(3)join方法当某个(A)线程等待另一个线程(B)执行结束后,才继续执行时,使用join方法。A的 run方法调用b.join()。下面为代码示例。package com.px1987.j2se.thread.join;class FatherThread implements Runnable public void run() System.out.println(爸爸想抽烟,发现烟抽完了);System.out.println(爸爸让儿子去买包红塔山);Thread son = new Thread(new SonThread();son.start();System.out.println(爸爸等儿子买烟回来);try /join含义:等待son线程执行完毕,father线程才继续执行son.join(); catch (InterruptedException e) System.out.println(爸爸出门去找儿子跑哪去了);System.exit(1);System.out.println(爸爸高兴的接过烟开始抽,并把零钱给了儿子);package com.px1987.j2se.thread.join;class SonThread implements Runnable public void run() String tabs=tttttt;System.out.println(tabs+儿子出门去买烟);System.out.println(tabs+儿子买烟需要10分钟);try for (int i = 0; i 10;) Thread.sleep(1000);System.out.println(tabs+儿子出去第 + +i + 分钟); catch (InterruptedException e) e.printStackTrace();System.out.println(tabs+儿子买烟回来了);package com.px1987.j2se.thread.join;public class JoinTest public static void main(String args) System.out.println(爸爸和儿子的故事); Thread father = new Thread(new FatherThread();father.start();/try /Thread.sleep(5000);/ catch (InterruptedException e) /e.printStackTrace();/errupt();该程序的运行结果如下:爸爸和儿子的故事爸爸想抽烟,发现烟抽完了爸爸让儿子去买包红塔山爸爸等儿子买烟回来儿子出门去买烟儿子买烟需要10分钟儿子出去第1分钟儿子出去第2分钟儿子出去第3分钟儿子出去第4分钟儿子出去第5分钟儿子出去第6分钟儿子出去第7分钟儿子出去第8分钟儿子出去第9分钟儿子出去第10分钟儿子买烟回来了爸爸高兴的接过烟开始抽,并把零钱给了儿子当时间来到儿子出去买烟的时候,Father线程调用interrupt方法就会打断son线程的正常执行,从而father线程也就不必等待son线程执行完毕再行动了,运行结果如下:爸爸和儿子的故事爸爸想抽烟,发现烟抽完了爸爸让儿子去买包红塔山爸爸等儿子买烟回来儿子出门去买烟儿子买烟需要10分钟儿子出去第1分钟儿子出去第2分钟儿子出去第3分钟儿子出去第4分钟爸爸出门去找儿子跑哪去了6.4线程的调度和优先级1、线程的基本信息方 法功 能isAlive()判断线程是否还“活”着,即线程是否还未终止。getPriority()获得线程的优先级数值setPriority()设置线程的优先级数值setName()给线程一个名字getName()取得线程的名字currentThread()取得当前正在运行的线程对象,也就是取得自己本身2、操作线程的基本信息代码示例package com.px1987.j2se.thread.priority;public class ThreadInfoTest public static void main(String argc) throws Exception Runnable r = new MyThread();Thread t = new Thread(r, Name test);t.start();System.out.println(name is: + t.getName();Thread.currentThread().sleep(5000);System.out.println(t.isAlive();System.out.println(over!);class MyThread implements Runnable public void run() for (int i = 0; i 100; i+)System.out.println(i);该程序的运行结果如下:name is: Name test0123. . .979899falseover!3、线程的优先级(1)优先级(共10级):它们决定线程执行的先后次序(优先级高者先执行)并可以通过Thread类中的setPriority()和getPriority()方法来改变和获取优先级。典型的优先级码n Thread.MIN_PRIORITY (1级)n Thread.MAX_PRIORITY(10级)n Thread.NORM_PRIORITY(5级)(2)调度规则Java是不支持线程时间片轮换的调度模型,而采用的是线程优先级高低的抢占调度模型。具有高优先级的线程可以抢占低优先级线程运行的机会。高优先级的线程将始终获得线程执行时间。但是这也不是绝对的,java线程调度器有可能会调用长期处于等待的线程进行执行,所以不要依靠线程的高优先级抢占模型去完成某些功能。Java线程调度器支持不同优先级线程的抢先方式,但其本身不支持相同优先级线程的时间片轮换。但是如果java运行时系统所在的操作系统(如windows2000)支持时间片的轮换,则线程调度器就支持相同优先级线程的时间片轮换。(3)代码示例package com.px1987.j2se.thread.priority;public class ThreadPriorityTest public static void main(String args) Thread t1 = new Thread(new MyThread2(), t1);Thread t2 = new Thread(new MyThread2(), t2);t1.setPriority(1);t2.setPriority(10);t1.start();t2.start();class MyThread2 extends Thread public void run() for (int i = 0; i 10; i+) System.out.println(Thread.currentThread().getName() + : + i); yield();该程序的运行结果如下:t1: 0t2: 0t2: 1t2: 2t2: 3t2: 4t2: 5t2: 6t2: 7t2: 8t2: 9t1: 1t1: 2t1: 3t1: 4t1: 5t1: 6t1: 7t1: 8t1: 96.5线程同步互斥1、线程同步互斥的一个示例多个线程同时访问或操作同一资源时,很容易出现数据前后不一致的问题。请看下面的例子:男孩拿着折子去北京银行海淀分行取钱女孩拿着男孩的银行卡去西单百货疯狂购物男孩走到柜台钱询问帐户余额银行的业务员小姐亲切地告诉他:您还有10000元!。女孩看上了一件时髦的衣裳,准备买下男孩在思考要取多少钱呢?女孩到收银台准备刷卡消费收银台刷卡机读取银行卡余额为10000元女孩买衣服刷卡消费5000元消费清单打印出来,消费:5000元 余额:5000元女孩离开商场男孩思考了1毫秒男孩决定取5000元银行的业务员小姐为男孩办理相关业务手续交易完成银行的业务员小姐告诉男孩:您的余额为5000元。男孩离开银行男孩帐户中一共有10000元,男孩拿着存折从银行取走5000元,女孩拿着男孩的银行卡购物刷卡消费5000元,最后男孩的帐户里却还剩5000元。显然这是不正确的,但是为什么会发生这样的情况呢?我们可以这样分析:男孩可以看作是一条线程,女孩也可以看作是一条线程,在同一时刻,两个线程都操作了同一个资源,那就是男孩的帐户。男孩从查看帐户余额到取走现金应该被看作是个原子性操作,是不可再分的,然而当男孩查看完余额正思考取多少钱的时候,女孩购物消费了5000元,也就是说女孩这条线程打断了男孩这条线程所要执行的任务。所以男孩刚查看完的余额10000元就不正确了,最终导致帐户中少减了5000元。为了避免这样的事情发生,我们要保证线程同步互斥,所谓同步互斥就是:并发执行的多个线程在某一时间内只允许一个线程在执行以访问共享数据2、Java中线程互斥的实现机制由多线程带来的性能改善是以可靠性为代价的,所以编程出线程安全的类代码是十分必要的。当多个线程可以访问共享资源(调用单个对象的属性和方法,对数据进行读、写、修改、删除等操作)时,应保证同时只有一个线程访问共享数据,Java对此提出了有效的解决方案同步锁。任何线程要进入同步互斥方法(访问共享资源的方法或代码段)时,就必须得到这个共享资源对象的锁,线程进入同步互斥方法后其它线程则不能再进入同步互斥方法,直到拥有共享资源对象锁的线程执行完同步互斥方法释放了锁,下一个线程才能进入同步互斥方法被执行。Java的这一线程互斥的实现机制可以用一个最通俗的比方来说明:比如公共卫生间就是一个共享资源,每个人都可以使用,但又不能同时使用,所以卫生间里有一把锁。一个人进去了,会把门锁上,其他人就不能进去。当Ta出来的时候,要打开锁,下一个人才能继续使用。3、利用Synchronized关键字用于修饰同步互斥方法(1)同步互斥方法 public synchronized void method() /允许访问控制的代码 (2)同步互斥代码块 synchronized(syncObject) /允许访问控制的代码(3)锁定整个类public synchronized class SyncObject 由于synchronized 块可以针对任意的代码块,且可任意指定上锁的对象,因此灵活性较高。但要注意:l synchronized可以用来限定一个方法或一小段语句或整个类(该类中的所有方法都是synchronized方法)l 将访问共享数据的代码设计为synchronized方法l 由于可以通过 private 关键字来保证数据对象只能被方法访问,所以只需针对方法提出一套同步锁定机制。通过synchronized 方法来控制对类中的成员变量(共享数据)的访问。l 编写线程安全的代码会使系统的总体效率会降低,要适量使用l 只有某一个线程的synchronized方法执行完后其它线程的synchronized方法才能被执行。l 当前时间,只有一个线程访问被锁定的代码段,但不能保证其他线程去访问其他没有被锁定的代码段。因此所有对共享资源进行操作的代码段都应该加锁。l 对数据库操作时,修改数据的线程要加锁,而读数据的线程可以不加锁有了这种解决方案,我们用线程安全的代码来重新实现一下男孩和女孩取钱的故事。以下是核心代码:package com.px1987.j2se.thread.synchronous.v2;/* 帐户类 */public class Account /* 余额 */private int balance;public Account(int balance) this.balance = balance;package com.px1987.j2se.thread.synchronous.v2;/* 男孩类,实现Runnable接口*/public class Boy implements Runnable /* 银行帐户*/Account account;public Boy(Account account) this.account = account;/* 男孩拿着折子去北京银行海淀分行取钱*/public void run() System.out.println(男孩拿着折子去北京银行海淀分行取钱);synchronized (account) System.out.println(男孩走到柜台钱询问帐户余额);int balance = account.getBalance();System.out.println(银行的业务员小姐亲切地告诉他:您还有 + balance + 元!。);try System.out.println(男孩在思考要取多少钱呢?);Thread.sleep(1);System.out.println(男孩思考了1毫秒); catch (InterruptedException e) e.printStackTrace();int money = 5000;System.out.println(男孩决定取 + money + 元);System.out.println(银行的业务员小姐为男孩办理相关业务手续);account.setBalance(balance - money);System.out.println(交易完成);System.out.println(银行的业务员小姐告诉男孩:您的余额为 + account.getBalance()+ 元。);System.out.println(男孩离开银行);package com.px1987.j2se.thread.synchronous.v2;/* 女孩类,实现runnable接口*/public class Girl implements Runnable /* 女孩持有男孩的银行卡*/Account account;public Girl(Account account) this.account = account;/* 女孩拿着小军的银行卡去西单百货疯狂购物*/public void run() String tabs = tttttt;System.out.println(tabs + 女孩拿着小军的银行卡去西单百货疯狂购物);System.out.println(tabs + 女孩看上了一件时髦的衣裳,准备买下);synchronized (account) System.out.println(tabs + 女孩到收银台准备刷卡消费);int balance = account.getBalance();System.out.println(tabs + 收银台刷卡机读取银行卡余额为 + balance + 元);int payout = 5000;System.out.println(tabs + 女孩买衣服刷卡消费 + payout + 元);account.setBalance(balance - payout);System.out.println(tabs + 消费清单打印出来,消费: + payout + 元 + 余额:+ account.getBalance() + 元);System.out.println(tabs + 女孩离开商场);package com.px1987.j2se.thread.synchronous.v2;public class Bank public static void main(String args) Account account=new Account(10000);Thread boyThread=new Thread(new Boy(account);Thread girlThread=new Thread(new Girl(account);boyThread.start();girlThread.start();修改后的代码运行结果如下图:男孩拿着折子去北京银行海淀分行取钱女孩拿着小军的银行卡去西单百货疯狂购物女孩看上了一件时髦的衣裳,准备买下女孩到收银台准备刷卡消费收银台刷卡机读取银行卡余额为10000元女孩买衣服刷卡消费5000元消费清单打印出来,消费:5000元 余额:5000元女孩离开商场男孩走到柜台钱询问帐户余额银行的业务员小姐亲切地告诉他:您还有5000元!。男孩在思考要取多少钱呢?男孩思考了1毫秒男孩决定取5000元银行的业务员小姐为男孩办理相关业务手续交易完成银行的业务员小姐告诉男孩:您的余额为0元。男孩离开银行从结果中可以看出来,男孩从查看余额到取钱,女孩没有操作帐户,所以最后的余额是正确的。4、线程死锁使用互斥锁容易产生死锁问题。比如:一个线程需要锁定两个对象才能完成,线程1拥有对象A的锁,线程1如果再拥有对象B的锁就能完成操作,线程2拥有对象B的锁,线程2如果再拥有对象A的锁就能完成操作。很不幸的是线程1执行不下去了,因为线程1等待的资源对象B被线程2锁住了,线程2也执行不下去了,因为线程2等待的资源对象A被线程1锁住了,这样就造成了死锁。阅读一段文字:由多线程带来的性能改善是以可靠性为代价的,主要是因为有可能产生线程死锁。死锁是这样一种情形:多个线程同时被阻塞,它们中的一个或者全部都在等待某个资源被释放。由于线程被无限期地阻塞,因此程序不能正常运行。简单的说就是:线程死锁时,第一个线程等待第二个线程释放资源,而同时第二个线程又在等待第一个线程释放资源。这里举一个通俗的例子:如在人行道上两个人迎面相遇,为了给对方让道,两人同时向一侧迈出一步,双方无法通过,又同时向另一侧迈出一步,这样还是无法通过。假设这种情况一直持续下去,这样就会发生死锁现象。 导致死锁的根源在于不适当地运用“synchronized”关键词来管理线程对特定对象的访问。“synchronized”关键词的作用是,确保在某个时刻只有一个线程被允许执行特定的代码块,因此,被允许执行的线程首先必须拥有对变量或对象的排他性访问权。当线程访问对象时,线程会给对象加锁,而这个锁导致其它也想访问同一对象的线程被阻塞,直至第一个线程释放它加在对象上的锁。(1)死锁问题的一个代码示例package com.px1987.j2se.thread.DeadLock;class Thread1 implements Runnable private Object a;private Object b;public Thread1(Object a, Object b) super();this.a = a;this.b = b;public void run() synchronized (a) System.out.println(Thread1获得对象a的锁);try Thread.sleep(1); catch (InterruptedException e) e.printStackTrace();synchronized (b) System.out.println(Thread1获得对象b的锁);try Thread.sleep(1); catch (InterruptedException e) e.printStackTrace();package com.px1987.j2se.thread.DeadLock;class Thread2 implements Runnable private Object a;private Object b;public Thread2(Object a, Object b) super();this.a = a;this.b = b;public void run() synchronized (b) System.out.println(Thread2获得对象b的锁);try Thread.sleep(1);catch (InterruptedException e) e.printStackTrace();synchronized (a) System.out.println(Thread2获得对象a的锁);try Thread.sleep(1); catch (InterruptedException e) e.printStackTrace();package com.px1987.j2se.thread.DeadLock;public class TestDeadLock public static void main(String args) Object a=new Object();Object b=new Object();Thread thread1=new Thread(new Thread1(a,b);Thread thread2=new Thread(new Thread2(a,b);thread1.start();thread2.start();下面是运行结果:(2)死锁问题的另一个代码示例package com.px1987.j2se.thread.DeadLock;public class ThreadDeadLock public static void main(String args) ThreadOne threadOne=new ThreadOne();ThreadTwo threadTwo=new ThreadTwo();String s1=s;String s2=sss;threadOne.op1=s1;threadTwo.op1=s1;threadOne.op2=s2;threadTwo.op2=s2;threadOne.start();threadTwo.start();class ThreadOne extends ThreadString op1;String op2;public void run()/ 同步中又有同步,就可能死锁synchronized(op1)System.out.println(Thread.currentThread().getName()+锁定op1);try Thread.sleep(1000); catch (InterruptedException e) e.printStackTrace();synchronized(op2)System.out.println(Thread.currentThread().getName()+锁定op2);class ThreadTwo extends ThreadString op1;String op2;public void run()synchronized(op2)System.out.println(Thread.currentThread().getName()+锁定op2);try Thread.sleep(1000); catch (InterruptedException e) e.printStackTrace();synchronized(op1)System.out.println(Thread.currentThread().getName()+锁定op1);6.6生产者消费者问题1、生产者消费者问题的示例生产者消费者问题也是一个典型的线程问题。我们举一个这方面的实例来说明:在一个果园里,有农夫和小孩,农夫会不停的采摘水果放入果园中心的一个水果筐直到水果筐满,而小孩会不停的从水果筐里拿水

温馨提示

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

评论

0/150

提交评论