Java多线程程序设计初步入门

Java多线程程序设计初步入门,第1张

在Java语言产生前 传统的程序设计语言的程序同一时刻只能单任务 *** 作 效率非常低 例如程序往往在接收数据输入时发生阻塞 只有等到程序获得数据后才能继续运行 随着Internet的迅猛发展 这种状况越来越不能让人们忍受 如果网络接收数据阻塞 后台程序就处于等待状态而不继续任何 *** 作 而这种阻塞是经常会碰到的 此时CPU资源被白白的闲置起来 如果在后台程序中能够同时处理多个任务 该多好啊!应Internet技术而生的Java语言解决了这个问题 多线程程序是Java语言的一个很重要的特点 在一个Java程序中 我们可以同时并行运行多个相对独立的线程 例如 我们如果创建一个线程来进行数据输入输出 而创建另一个线程在后台进行其它的数据处理 如果输入输出线程在接收数据时阻塞 而处理数据的线程仍然在运行 多线程程序设计大大提高了程序执行效率和处理能力

线程的创建

我们知道Java是面向对象的程序语言 用Java进行程序设计就是设计和使用类 Java为我们提供了线程类Thread来创建线程 创建线程与创建普通的类的对象的 *** 作是一样的 而线程就是Thread类或其子类的实例对象 下面是一个创建启动一个线程的语句

Thread thread =new Thread()file://声明一个对象实例 即创建一个线程

Thread run()file://用Thread类中的run()方法启动线程

从这个例子 我们可以通过Thread()构造方法创建一个线程 并启动该线程 事实上 启动线程 也就是启动线程的run()方法 而Thread类中的run()方法没有任何 *** 作语句 所以这个线程没有任何 *** 作 要使线程实现预定功能 必须定义自己的run()方法 Java中通常有两种方式定义run()方法

通过定义一个Thread类的子类 在该子类中重写run()方法 Thread子类的实例对象就是一个线程 显然 该线程有我们自己设计的线程体run()方法 启动线程就启动了子类中重写的run()方法

通过Runnable接口 在该接口中定义run()方法的接口 所谓接口跟类非常类似 主要用来实现特殊功能 如复杂关系的多重继承功能 在此 我们定义一个实现Runnable() 接口的类 在该类中定义自己的run()方法 然后以该类的实例对象为参数调用Thread类的构造方法来创建一个线程

线程被实际创建后处于待命状态 激活(启动)线程就是启动线程的run()方法 这是通过调用线程的start()方法来实现的

下面一个例子实践了如何通过上述两种方法创建线程并启动它们

// 通过Thread类的子类创建的线程   class thread extends Thread { file://自定义线程的run()方法   public void run() {   System out println( Thread is running… )}   }   file://通过Runnable接口创建的另外一个线程 class thread implements Runnable   { file://自定义线程的run()方法 public void run() {   System out println( Thread is running… )}   }   file://程序的主类   class Multi_Thread file://声明主类 {   plubic static void mail(String args[]) file://声明主方法 {   thread threadone=new thread ()file://用Thread类的子类创建线程   Thread threado=new Thread(new thread ())file://用Runnable接口类的对象创建线程   threadone start()threado start()file://strat()方法启动线程 }   }

运行该程序就可以看出 线程threadone和threado交替占用CPU 处于并行运行状态 可以看出 启动线程的run()方法是通过调用线程的start()方法来实现的(见上例中主类) 调用start()方法启动线程的run()方法不同于一般的调用方法 调用一般方法时 必须等到一般方法执行完毕才能够返回start()方法 而启动线程的run()方法后 start()告诉系统该线程准备就绪可以启动run()方法后 就返回start()方法执行调用start()方法语句下面的语句 这时run()方法可能还在运行 这样 线程的启动和运行并行进行 实现了多任务 *** 作

线程的优先级

对于多线程程序 每个线程的重要程度是不尽相同 如多个线程在等待获得CPU时间时 往往我们需要优先级高的线程优先抢占到CPU时间得以执行 又如多个线程交替执行时 优先级决定了级别高的线程得到CPU的次数多一些且时间多长一些 这样 高优先级的线程处理的任务效率就高一些

Java中线程的优先级从低到高以整数 ~ 表示 共分为 级 设置优先级是通过调用线程对象的setPriority()方法 如上例中 设置优先级的语句为

thread threadone=new thread ()file://用Thread类的子类创建线程

Thread threado=new Thread(new thread ())file://用Runnable接口类的对象创建线程

threadone setPriority( )file://设置threadone的优先级

threado setPriority( )file://设置threado的优先级

threadone start()threado start()file://strat()方法启动线程

这样 线程threadone将会优先于线程threado执行 并将占有更多的CPU时间 该例中 优先级设置放在线程启动前 也可以在启动后进行设置 以满足不同的优先级需求

线程的(同步)控制

一个Java程序的多线程之间可以共享数据 当线程以异步方式访问共享数据时 有时候是不安全的或者不和逻辑的 比如 同一时刻一个线程在读取数据 另外一个线程在处理数据 当处理数据的线程没有等到读取数据的线程读取完毕就去处理数据 必然得到错误的处理结果 这和我们前面提到的读取数据和处理数据并行多任务并不矛盾 这儿指的是处理数据的线程不能处理当前还没有读取结束的数据 但是可以处理其它的数据

如果我们采用多线程同步控制机制 等到第一个线程读取完数据 第二个线程才能处理该数据 就会避免错误 可见 线程同步是多线程编程的一个相当重要的技术

在讲线程的同步控制前我们需要交代如下概念

用Java关键字synchonized同步对共享数据 *** 作的方法

在一个对象中 用synchonized声明的方法为同步方法 Java中有一个同步模型 监视器 负责管理线程对对象中的同步方法的访问 它的原理是 赋予该对象唯一一把 钥匙 当多个线程进入对象 只有取得该对象钥匙的线程才可以访问同步方法 其它线程在该对象中等待 直到该线程用wait()方法放弃这把钥匙 其它等待的线程抢占该钥匙 抢占到钥匙的线程后才可得以执行 而没有取得钥匙的线程仍被阻塞在该对象中等待

file://声明同步的一种方式 将方法声明同步

class store  {public synchonized void store_in(){… }public synchonized void store_out(){  … }}

  利用wait() notify()及notifyAll()方法发送消息实现线程间的相互联系

Java程序中多个线程通过消息来实现互动联系的 这几种方法实现了线程间的消息发送 例如定义一个对象的synchonized 方法 同一时刻只能够有一个线程访问该对象中的同步方法 其它线程被阻塞 通常可以用notify()或notifyAll()方法唤醒其它一个或所有线程 而使用wait()方法来使该线程处于阻塞状态 等待其它的线程用notify()唤醒

一个实际的例子就是生产和销售 生产单元将产品生产出来放在仓库中 销售单元则从仓库中提走产品 在这个过程中 销售单元必须在仓库中有产品时才能提货 如果仓库中没有产品 则销售单元必须等待

程序中 假如我们定义一个仓库类store 该类的实例对象就相当于仓库 在store类中定义两个成员方法 store_in() 用来模拟产品制造者往仓库中添加产品 strore_out()方法则用来模拟销售者从仓库中取走产品 然后定义两个线程类 customer类 其中的run()方法通过调用仓库类中的store_out()从仓库中取走产品 模拟销售者 另外一个线程类producer中的run()方法通过调用仓库类中的store_in()方法向仓库添加产品 模拟产品制造者 在主类中创建并启动线程 实现向仓库中添加产品或取走产品

如果仓库类中的store_in() 和store_out()方法不声明同步 这就是个一般的多线程 我们知道 一个程序中的多线程是交替执行的 运行也是无序的 这样 就可能存在这样的问题

仓库中没有产品了 销售者还在不断光顾 而且还不停的在 取 产品 这在现实中是不可思义的 在程序中就表现为负值 如果将仓库类中的stroe_in()和store_out()方法声明同步 如上例所示 就控制了同一时刻只能有一个线程访问仓库对象中的同步方法 即一个生产类线程访问被声明为同步的store_in()方法时 其它线程将不能够访问对象中的store_out()同步方法 当然也不能访问store_in()方法 必须等到该线程调用wait()方法放弃钥匙 其它线程才有机会访问同步方法

lishixinzhi/Article/program/Java/gj/201311/27301

我们知道 win 或winNT都是 多线程 的 *** 作系统 在DELPHI . 中 我们可以充分利用这一特性 编写出 多线程 的应用程序 对以往在DOS或 位windows下写程序的人来说 多线程 仍然是陌生的 但如同以前我们从DOS下的单任务过渡到windows . 下的多任务 如今我们又必须过渡到 多线程 领域 毕竟计算机时代是在不断发展的 不过 幸运的是 在DELPHI . 下进行多线程程序设计并不需要我们去学习庞大的WIN API函数 我们可以利用DELPHI下标准的多线程类TThread来完成我们的工作  TThread是一个abstract(抽象)类 也就是说 并不需要根据TThread来声明变量(而且根据TThread声明的变量也是完全无用) 我们要做的是把TThread作为基类 用继承的形式来生成子类 实际上 根据TThread来写多线程应用程序是非常容易的 下面就是一个基本的继承TThread生成的多线程类 QuerThrd.Pas unitQuerThrd interface uses Classes DBTables type TQueryThreadΚclass(TThread) private fQuery tQuery protected procedureExecute override public constructorCreate(Suspended Boolean Query TQuery) end implementation constructor TQueryThread.Create(Suspended Boolean Query TQuery) begin inheritedCreate(Suspended) fQuery ΚQuery FreeOnTerminate ΚTrue end procedureTQueryThread.Execute begin fQuery.Open end end. 在上面这个简单的例子中 我们构造了一个TThread的子类TQueryThread 用于在后台执行数据库查询 在该类的Create函数中 传递了两个参数Suspended和Query 其中Suspended用于控制线程的运行 如果Suspend为真 TQueryThread类的线程在建立后将立即被悬挂 一直到运行了Resume方法 该线程才会继续执行 Query参数用于接受一个已经存在的Query控件(在窗体中真正的Query控件)而使它在多线程的情况下运行 Execute是最重要的过程 它是类TQueryThread的执行部分 所有需要在这个多线程类中运行的语句都必须写在这个过程里 实际上构造自己的多线程类时 并不需要输入所有的这些代码 选择DELPHI的File菜单下的new选项 再选 TThreadObject 项目 DELPHI就会为你构造基本的程序模块 然后我们可以根据需要再做相应的修改 进程的执行 假设我们已经建立了一个窗体FORM 窗体中有我们将要使用的查询控件Query 那么我们在该单元的USES部分加入上面写的QuerThrd单元 procedureTForm .Button Click(Sender TObject) begin {建立一个运行的进程} TQueryThread.Create(False Query ) end 如果这个过程被执行 那么窗体中的查询控件Query 就会自动在多线程的环境下运行查询 注意TQueryThread类中只有Create而没有Free 动态建立类以后又忘记删除是我们常犯的错误之一 不过在这里由于我们指定了FreeOnTerminate(运行完即删除)为真 所以当Execute里的语句执行完后 TQueryThread类占据的内存控件将被自动释放 然而还有一个问题值得我们注意 由于同一时刻可以有多个线程同时运行 那么我们还必须解决好同步的问题 如果几个多线程程序之间没有任何关联 那么它们之间也不会有任何冲突 但实际上 可能同时运行几个多线程的数据库应用程序 由于需要共享相同的数据库资源 我们还需要为Query 增加一个Tsession控件 其实 虽然我们也许没有亲自使用过Session控件 但实际上 在所有的数据库访问时DELPHI都会自动建立一个临时的Session控件 使用完后又动态地删除掉它 在平常的数据库编程时 用不着我们亲自来 *** 作 但在数据库多线程执行的情况下 为了不相互冲突 我们必须为每个数据库访问都定制自己的Session控件 这个步骤非常简单 我们只需要在窗体中增加一个Session控件 然后给它的属性 Sessionname 写一个任意的名字 并再在Query 的 Sessionname 中写一个相同的名字 这样我们的数据库程序就安全了 另一类需要解决同步问题的是那些对VCL资源进行 *** 作的程序 这类的程序非常多 好在解决的方法也非常简单 我们可以看下面这样一个程序 unitBncThrd interface uses WinProcs Classes Graphics ExtCtrls type TBounceThreadΚclass(TThread) private FShape TShape FXSpeed Integer FYSpeed Integer procedureMoveShape protected procedureExecute override public constructorCreate(Suspended Boolean Shape TShape XSpeed YSpeed Integer) propertyShape TShapereadFShape end implementation procedureTBouad.MoveShape var MaxHeight MaxWidth Integer begin withFShapedo begin Left ΚLeft+FXSpeed Top ΚTop+FYSpeed if(LeftΙ )or (Left+WidthΛParent.Width)then FXSpeed ΚFXSpeed*- if(TopΙ )or (Top+HeightΛParent.Height)then FYSpeed ΚFYSpeed*- end end procedureTBounceThread.Execute begin WhilenotTerminateddo begin Synchronize(MoveShape) end end constructorTBounceThread.Create(Suspended Boolean Shape TShape XSpeed YSpeed Integer) begin inheritedCreate(Suspended) FShape ΚShape FXSpeed ΚXSpeed {X轴走向的速度} FYSpeed ΚYSpeed {Y轴走向的速度} FreeOnTerminate ΚTrue end end. 这是一个多线程的碰碰球游戏 你可以有多个不同的球 它们分属不同的线程 各自独立的在屏幕上碰撞 显然 由于多个球运行的显示会同时 *** 作VCL资源 为了安全 我们在Execute过程中的执行部分加入了Synchronize(MoveShape)来调用MoveShape过程 实际上 在任何需要 *** 作VCL资源的地方 例如窗体 位图 都应加入Synchronize调用 执行时我们可以新建一个程序 然后在USES部分加入以上的BncThrd单元 再在它的窗体FORM 上加入两个Shape控件Shape 和Shape Shape 可以是一个矩形而Shape 是一个圆 加入以下的代码就可以让矩形和圆动起来 procedureTForm .Button Click(Sender TObject) begin TBounceThread.Create(False Shape ) TBounceThread.Create(False Shape ) end lishixinzhi/Article/program/Delphi/201311/25059

多线程概述

进程和线程都是 *** 作系统的概念。进程是应用程序的执行实例,每个进程是由私有的虚拟地址空间、代码、数据和其它各种系统资源组成,进程在运行过程中创建的资源随着进程的终止而被销毁,所使用的系统资源在进程终止时被释放或关闭。

线程是进程内部的一个执行单元。系统创建好进程后,实际上就启动执行了该进程的主执行线程,主执行线程以函数地址形式,比如说main或WinMain函数,将程序的启动点提供给Windows系统。主执行线程终止了,进程也就随之终止。

每一个进程至少有一个主执行线程,它无需由用户去主动创建,是由系统自动创建的。用户根据需要在应用程序中创建其它线程,多个线程并发地运行于同一个进程中。每个线程具有自己的堆栈和自己的 CPU 寄存器副本。其他资源(如文件、静态数据和堆内存)由进程中的所有线程共享。所以线程间的通讯非常方便,多线程技术的应用也较为广泛。但是使用这些公共资源的线程必须同步。Win32 提供了几种同步资源的方式,包括信号、临界区、事件和互斥体。

每个进程都有私有的虚拟地址空间,进程的所有线程共享同一地址空间。每个线程被CPU分配一个时间片,一旦被激活,它正常运行直到时间片耗尽并被挂起,此时, *** 作系统选择另一个线程进行运行。通过时间片轮转,又出于各个时间片很小(20毫秒级),看起来就像多个线程同时在工作。实际上,只有在多处理器系统上才是真正的在可得到的处理器上同时运行多个线程。基于Win32的应用程序可以通过把给定进程分解(或创建)多个线程挖掘潜在的CPU时间,而且还可以加强应用程序,以使用户提高效率,加强反应能力以及进行后台辅助处理。

在Windows *** 作系统中,Win32应用程序可以在Windows平台上运行多个实例,每个应用程序实例都是一个独立的进程,而一个进程可以由不止一个线程来实现。对于一个进程来说,当应用程序有几个任务要同时运行时,建立多个线程是有用的。如打印时,利用多线程机制实现多线程,就可在需要打印时创建一个负责完成打印功能的打印线程。创建打印线程之后,系统就变成了多线程。当进行打印时,CPU轮换着分配给这两个线程时间片,所以打印和其他功能一起同时在运行,这就充分利用了CPU处理打印工作之外的空闲时间片,并且避免了用户长久地等待打印时间。这就是所谓的由多线程来实现的多任务,在进行打印任务的同时又可以进行别的任务。要说明的一点是,目前大多数的计算机都是单处理器(CPU)的,为了运行所有这些线程, *** 作系统为每个独立线程安排一些CPU时间, *** 作系统以轮换方式向线程提供时间片,这就给人一种假象,好象这些线程都在同时运行。由此可见,如果两个非常活跃的线程为了抢夺对CPU的控制权,在线程切换时会消耗很多的CPU资源,反而会降低系统的性能。这一点在多线程编程时应该注意。

Win32 SDK函数支持进行多线程的程序设计,并提供了 *** 作系统原理中的各种同步、互斥和临界区等 *** 作。Visual C++ 6.0中,使用MFC类库也实现了多线程的程序设计,线程被分为工作者线程(Worker Thread)和用户界面线程(User Interface Thread)两大类。前者常用于处理后台任务,执行这些后台任务并不会耽搁用户对应用程序的使用,即用户 *** 作无需等待后台任务的完成。后者常用来独立的处理用户输入和相应用户的事件。其中用户界面线程的特点是拥有单独的消息队列,可以具有自己的窗口界面,能够对用户输入和事件做出响应。在应用程序中,根据用户界面线程具有消息队列这一特点,可以使之循环等待某一事件发生后再进行处理。由于Windows95时抢先式多任务的 *** 作系统,即使一个线程因等待某事件而阻塞,其他线程仍然可以继续执行。


欢迎分享,转载请注明来源:内存溢出

原文地址:https://54852.com/yw/10968998.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2023-05-12
下一篇2023-05-12

发表评论

登录后才能评论

评论列表(0条)

    保存