关于线程.docx

上传人:b****6 文档编号:5734437 上传时间:2022-12-31 格式:DOCX 页数:20 大小:58.24KB
下载 相关 举报
关于线程.docx_第1页
第1页 / 共20页
关于线程.docx_第2页
第2页 / 共20页
关于线程.docx_第3页
第3页 / 共20页
关于线程.docx_第4页
第4页 / 共20页
关于线程.docx_第5页
第5页 / 共20页
点击查看更多>>
下载资源
资源描述

关于线程.docx

《关于线程.docx》由会员分享,可在线阅读,更多相关《关于线程.docx(20页珍藏版)》请在冰豆网上搜索。

关于线程.docx

关于线程

JAVA的多线程浅析

一JAVA语言的来源、及特点

  

  在这个高速信息的时代,商家们纷纷把信息、产品做到Internet国际互连网页上。

再这些不寻常网页的背后,要属功能齐全、安全可靠的编程语言,Java是当之无愧的。

Java是由SunMicrosystem开发的一种功能强大的新型程序设计语言。

是与平台无关的编程语言。

它是一种简单的、面象对象的、分布式的、解释的、键壮的、安全的、结构的中立的、可移植的、性能很优异的、多线程的、动态的、语言。

  

  Java自问世以后,以其编程简单、代码高效、可移植性强,很快受到了广大计算机编程人士的青睐。

Java语言是Internet上具有革命性的编程语言,它具有强大的动画、多媒体和交互功能,他使WorldWeb进入了一个全新的时代。

Java语言与C++极为类似,可用它来创建安全的、可移植的、多线程的交互式程序。

另外用Java开发出来的程序与平台无关,可在多种平台上运行。

后台开发,是一种高效、实用的编程方法。

人们在屏幕前只能看到例如图案、计算的结果等。

实际上操作系统往往在后台来调度一些事件、管理程序的流向等。

例如操作系统中的堆栈,线程间的资源分配与管理,内存的创建、访问、管理等。

可谓举不盛举。

下面就多线程来谈一谈。

  

  

  

  二JAVA的多线程理论

  

  2.1引入

  

  Java提供的多线程功能使得在一个程序里可同时执行多个小任务。

线程有时也称小进程是一个大进程里分出来的小的独立的进程。

因为Java实现的多线程技术,所以比C和C++更键壮。

多线程带来的更大的好处是更好的交互性能和实时控制性能。

当然实时控制性能还取决于系统本身(UNIX,Windows,Macintosh等),在开发难易程度和性能上都比单线程要好。

传统编程环境通常是单线程的,由于JAVA是多线程的。

尽管多线程是强大而灵巧的编程工具,但要用好却不容易,且有许多陷阱,即使编程老手也难免误用。

为了更好的了解线程,用办公室工作人员作比喻。

办公室工作人员就象CPU,根据上级指示做工作,就象执行一个线程。

在单线程环境中,每个程序编写和执行的方式是任何时候程序只考虑一个处理顺序。

用我们的比喻,就象办公室工作人员从头到尾不受打扰和分心,只安排做一个工作。

当然,实际生活中工作人员很难一次只有一个任务,更常见的是工作人员要同时做几件事。

老板将工作交给工作人员,希望工作人员做一这个工作,再做点那个工作,等等。

如果一个任务无法做下去了,比如工作人员等待另一部门的信息,则工作人员将这个工作放在一边,转入另一个工作。

一般来说,老板希望工作人员手头的各个任务每一天都有一些进展。

这样就引入了多线程的概念。

多线程编程环境与这个典型的办公室非常相似,同时给CPU分配了几个任务或线程。

和办公室人员一样,计算机CPU实际上不可能同一时间做几件事,而是把时间分配到不同的线程,使每个线程都有点进展。

如果一个线程无法进行,比如线程要求的键盘输入尚未取得,则转入另一线程的工作。

通常,CPU在线程间的切换非常迅速,使人们感觉到好象所有线程是同时进行的。

  

  任何处理环境,无论是单线程还是多线程,都有三个关键方面。

第一个是CPU,它实际上进行计算机活动;第二个是执行的程序的代码;第三个是程序操作的数据。

  

  在多线程编程中,每个线程都用编码提供线程的行为,用数据供给编码操作。

多个线程可以同时处理同一编码和数据,不同的线程也可能各有不同的编码和数据。

事实上编码和数据部分是相当独立的,需要时即可向线程提供。

因此经常是几个线程使用同一编码和不同的数据。

这个思想也可以用办公室工作人员来比喻。

会计可能要做一个部门的帐或几个或几个部门的帐。

任何情况的做帐的任务是相同的程序代码,但每个部门的数据是不同的。

会计可能要做整个公司的帐,这时有几个任务,但有些数据是共享的,因为公司帐需要来自各个部门的数据。

  

  多线程编程环境用方便的模型隐藏CPU在任务切换间的事实。

模型允许假装成有多个可用的CPU。

为了建立另一个任务,编程人员要求另一个虚拟CPU,指示它开始用某个数据组执行某个程序段。

下面我们来建立线程。

  

  建立线程

  

  在JAVA中建立线程并不困难,所需要的三件事:

执行的代码、代码所操作的数据和执行代码的虚拟CPU。

虚拟CPU包装在Thread类的实例中。

建立Thread对象时,必须提供执行的代码和代码所处理的数据。

JAVA的面向对象模型要求程序代码只能写成类的成员方法。

数据只能作为方法中的自动(或本地)变量或类的成员存在。

这些规则要求为线程提供的代码和数据应以类的实例的形式出现。

  

  PublicclassSimpleRunnableimplemantsRunable{

  

  PrivateStringmessage;

  

  Publicstaticvoidmain(Stringargs[]){

  

  SimpleRunnabler1=newSimpleRunnable(“Hello”);

  

  Threadt1=newThread(r1);

  

  t1.start();

  

  }

  

  publicSimpleRunnable(Stringmessage){

  

  this.message=message;

  

  }

  

  publicvoidrun(){

  

  for(;;){

  

  System.out.println(message);

  

  }

  

  }

  

  }

  

  线程开始执行时,它在publicvoidrun()方法中执行。

这种方法是定义的线程执行的起点,就象应用程序从main()开始、小程序从init()开始一样。

线程操作的本地数据是传入线程的对象的成员。

  

  首先,main()方法构造SimpleRunnable类的实例。

注意,实例有自己的数据,这里是一个String,初始化为”Hello”.由于实例r1传入Thread类构造器,这是线程运行时处理的数据。

执行的代码是实例方法run()。

  

  2.2线程的管理

  

  单线程的程序都有一个main执行体,它运行一些代码,当程序结束执行后,它正好退出,程序同时结束运行。

在JAVA中我们要得到相同的应答,必须稍微进行改动。

只有当所有的线程退出后,程序才能结束。

只要有一个线程一直在运行,程序就无法退出。

线程包括四个状态:

new(开始),running(运行),wait(等候)和done(结束)。

第一次创建线程时,都位于new状态,在这个状态下,不能运行线程,只能等待。

然后,线程或者由方法start开始或者送往done状态,位于done中的线程已经结束执行,这是线程的最后一个状态。

一旦线程位于这个状态,就不能再次出现,而且当JAVA虚拟机中的所有线程都位于done状态时,程序就强行中止。

当前正在执行的所有线程都位于running状态,在程序之间用某种方法把处理器的执行时间分成时间片,位于running状态的每个线程都是能运行的,但在一个给定的时间内,每个系统处理器只能运行一个线程。

与位于running状态的线程不同,由于某种原因,可以把已经位于waiting状态的线程从一组可执行线程中删除。

如果线程的执行被中断,就回到waiting状态。

用多种方法能中断一个线程。

线程能被挂起,在系统资源上等候,或者被告知进入休眠状态。

该状态的线程可以返回到running状态,也能由方法stop送入done状态,

  

  方法

  描述

  有效状态

  目的状态

  

  Start()

  开始执行一个线程

  New

  Running

  

  Stop()

  结束执行一个线程

  New或running

  Done

  

  Sleep(long)

  暂停一段时间,这个时间为给定的毫秒

  Running

  Wait

  

  Sleep(long,int)

  暂停片刻,可以精确到纳秒

  Running

  Wait

  

  Suspend()

  挂起执行

  Running

  Wait

  

  Resume()

  恢复执行

  Wait

  Running

  

  Yield()

  明确放弃执行

  Running

  Running

  

  

  2.3线程的调度

  

  线程运行的顺序以及从处理器中获得的时间数量主要取决于开发者,处理器给每个线程分配一个时间片,而且线程的运行不能影响整个系统。

处理器线程的系统或者是抢占式的,或者是非抢占式的。

抢占式系统在任何给定的时间内将运行最高优先级的线程,系统中的所有线程都有自己的优先级。

Thread.NORM_PRIORITY是线程的缺省值,Thread类提供了setPriority和getPriority方法来设置和读取优先权,使用setPriority方法能改变Java虚拟机中的线程的重要性,它调用一个整数,类变量Thread.MIN_PRIORITY和Thread.MAX_PRIORITY决定这个整数的有效范围。

Java虚拟机是抢占式的,它能保证运行优先级最高的线程。

在JAVA虚拟机中我们把一个线程的优先级改为最高,那么他将取代当前正在运行的线程,除非这个线程结束运行或者被一条休眠命令放入waiting状态,否者将一直占用所有的处理器的时间。

如果遇到两个优先级相同的线程,操作系统可能影响线程的执行顺序。

而且这个区别取决于时间片(timeslicing)的概念。

  

  管理几个线程并不是真正的难题,对于上百个线程它是怎样管理的呢?

当然可以通过循环,来执行每一个线程,但是这显然是冗长、乏味。

JAVA创建了线程组。

线程组是线程的一个谱系组,每个组包含的线程数不受限制,能对每个线程命名并能在整个线程组中执行(Suspend)和停止(Stop)这样的操作。

  

  2.4信号标志:

保护其它共享资源

  

这种类型的保护被称为互斥锁。

某个时间只能有一个线程读取或修改这个数据值。

在对文件尤其是信息数据库进行处理时,读取的数据总是多于写数据,根据这个情况,可以简化程序。

下面举一例,假设有一个雇员信息的数据库,其中包括雇员的地址和电话号码等信息,有时要进行修改,但要更多的还是读数据,因此要尽可能防止数据被破坏或任意删改。

我们引入前面互斥锁的概念,允许一个读取锁(redlock)和写入锁(writelock),可根据需要确定有权读取数据的人员。

 

一、认识多任务、多进程、单线程、多线程

要认识多线程就要从操作系统的原理说起。

以前古老的DOS操作系统(V6.22)是单任务的,还没有线程的概念,系统在每次只能做一件事情。

比如你在copy东西的时候不能rename文件名。

为了提高系统的利用效率,采用批处理来批量执行任务。

现在的操作系统都是多任务操作系统,每个运行的任务就是操作系统所做的一件事情,比如你在听歌的同时还在用MSN和好友聊天。

听歌和聊天就是两个任务,这个两个任务是“同时”进行的。

一个任务一般对应一个进程,也可能包含好几个进程。

比如运行的MSN就对应一个MSN的进程,如果你用的是windows系统,你就可以在任务管理器中看到操作系统正在运行的进程信息。

一般来说,当运行一个应用程序的时候,就启动了一个进程,当然有些会启动多个进程。

启动进程的时候,操作系统会为进程分配资源,其中最主要的资源是内存空间,因为程序是在内存中运行的。

在进程中,有些程序流程块是可以乱序执行的,并且这个代码块可以同时被多次执行。

实际上,这样的代码块就是线程体。

线程是进程中乱序执行的代码流程。

当多个线程同时运行的时候,这样的执行模式成为并发执行。

多线程的目的是为了最大限度的利用CPU资源。

Java编写程序都运行在在Java虚拟机(JVM)中,在JVM的内部,程序的多任务是通过线程来实现的。

每用java命令启动一个java应用程序,就会启动一个JVM进程。

在同一个JVM进程中,有且只有一个进程,就是它自己。

在这个JVM环境中,所有程序代码的运行都是以线程来运行。

一般常见的Java应用程序都是单线程的。

比如,用java命令运行一个最简单的HelloWorld的Java应用程序时,就启动了一个JVM进程,JVM找到程序程序的入口点main(),然后运行main()方法,这样就产生了一个线程,这个线程称之为主线程。

当main方法结束后,主线程运行完成。

JVM进程也随即退出。

对于一个进程中的多个线程来说,多个线程共享进程的内存块,当有新的线程产生的时候,操作系统不分配新的内存,而是让新线程共享原有的进程块的内存。

因此,线程间的通信很容易,速度也很快。

不同的进程因为处于不同的内存块,因此进程之间的通信相对困难。

实际上,操作的系统的多进程实现了多任务并发执行,程序的多线程实现了进程的并发执行。

多任务、多进程、多线程的前提都是要求操作系统提供多任务、多进程、多线程的支持。

在Java程序中,JVM负责线程的调度。

线程调度是值按照特定的机制为多个线程分配CPU的使用权。

调度的模式有两种:

分时调度和抢占式调度。

分时调度是所有线程轮流获得CPU使用权,并平均分配每个线程占用CPU的时间;抢占式调度是根据线程的优先级别来获取CPU的使用权。

JVM的线程调度模式采用了抢占式模式。

所谓的“并发执行”、“同时”其实都不是真正意义上的“同时”。

众所周知,CPU都有个时钟频率,表示每秒中能执行cpu指令的次数。

在每个时钟周期内,CPU实际上只能去执行一条(也有可能多条)指令。

操作系统将进程线程进行管理,轮流(没有固定的顺序)分配每个进程很短的一段是时间(不一定是均分),然后在每个线程内部,程序代码自己处理该进程内部线程的时间分配,多个线程之间相互的切换去执行,这个切换时间也是非常短的。

因此多任务、多进程、多线程都是操作系统给人的一种宏观感受,从微观角度看,程序的运行是异步执行的。

用一句话做总结:

虽然操作系统是多线程的,但CPU每一时刻只能做一件事,和人的大脑是一样的,呵呵。

二、Java与多线程

Java语言的多线程需要操作系统的支持。

Java虚拟机允许应用程序并发地运行多个执行线程。

Java语言提供了多线程编程的扩展点,并给出了功能强大的线程控制API。

在Java中,多线程的实现有两种方式:

扩展java.lang.Thread类

实现java.lang.Runnable接口

每个线程都有一个优先级,高优先级线程的执行优先于低优先级线程。

每个线程都可以或不可以标记为一个守护程序。

当某个线程中运行的代码创建一个新Thread对象时,该新线程的初始优先级被设定为创建线程的优先级,并且当且仅当创建线程是守护线程时,新线程才是守护程序。

当Java虚拟机启动时,通常都会有单个非守护线程(它通常会调用某个指定类的main方法)。

Java虚拟机会继续执行线程,直到下列任一情况出现时为止:

调用了Runtime类的exit方法,并且安全管理器允许退出操作发生。

非守护线程的所有线程都已停止运行,无论是通过从对run方法的调用中返回,还是通过抛出一个传播到run方法之外的异常。

三、扩展java.lang.Thread类

/**

*FileName:

TestMitiThread.java

*Createdby:

IntelliJIDEA.

*Copyright:

Copyright(c)2003-2006

*Company:

Lavasoft([url]

*Author:

leizhimin

*Modifier:

leizhimin

*DateTime:

2007-5-1710:

03:

12

*Readme:

通过扩展Thread类实现多线程

*/

publicclassTestMitiThread{

publicstaticvoidmain(String[]rags){

System.out.println(Thread.currentThread().getName()+"线程运行开始!

");

newMitiSay("A").start();

newMitiSay("B").start();

System.out.println(Thread.currentThread().getName()+"线程运行结束!

");

}

}

classMitiSayextendsThread{

publicMitiSay(StringthreadName){

super(threadName);

}

publicvoidrun(){

System.out.println(getName()+"线程运行开始!

");

for(inti=0;i<10;i++){

System.out.println(i+""+getName());

try{

sleep((int)Math.random()*10);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

System.out.println(getName()+"线程运行结束!

");

}

}

运行结果:

main线程运行开始!

main线程运行结束!

A线程运行开始!

0A

1A

B线程运行开始!

2A

0B

3A

4A

1B

5A

6A

7A

8A

9A

A线程运行结束!

2B

3B

4B

5B

6B

7B

8B

9B

B线程运行结束!

说明:

程序启动运行main时候,java虚拟机启动一个进程,主线程main在main()调用时候被创建。

随着调用MitiSay的两个对象的start方法,另外两个线程也启动了,这样,整个应用就在多线程下运行。

在一个方法中调用Thread.currentThread().getName()方法,可以获取当前线程的名字。

在mian方法中调用该方法,获取的是主线程的名字。

注意:

start()方法的调用后并不是立即执行多线程代码,而是使得该线程变为可运行态(Runnable),什么时候运行是由操作系统决定的。

从程序运行的结果可以发现,多线程程序是乱序执行。

因此,只有乱序执行的代码才有必要设计为多线程。

Thread.sleep()方法调用目的是不让当前线程独自霸占该进程所获取的CPU资源,以留出一定时间给其他线程执行的机会。

实际上所有的多线程代码执行顺序都是不确定的,每次执行的结果都是随机的。

四、实现java.lang.Runnable接口

/**

*通过实现Runnable接口实现多线程

*/

publicclassTestMitiThread1implementsRunnable{

publicstaticvoidmain(String[]args){

System.out.println(Thread.currentThread().getName()+"线程运行开始!

");

TestMitiThread1test=newTestMitiThread1();

Threadthread1=newThread(test);

Threadthread2=newThread(test);

thread1.start();

thread2.start();

System.out.println(Thread.currentThread().getName()+"线程运行结束!

");

}

publicvoidrun(){

System.out.println(Thread.currentThread().getName()+"线程运行开始!

");

for(inti=0;i<10;i++){

System.out.println(i+""+Thread.currentThread().getName());

try{

Thread.sleep((int)Math.random()*10);

}catch(InterruptedExceptione){

e.printStackTrace();

}

}

System.out.println(Thread.currentThread().getName()+"线程运行结束!

");

}

}

运行结果:

main线程运行开始!

Thread-0线程运行开始!

main线程运行结束!

0Thread-0

Thread-1线程运行开始!

0Thread-1

1Thread-1

1Thread-0

2Thread-0

2Thread-1

3Thread-0

3Thread-1

4Thread-0

4Thread-1

5Thread-0

6Thread-0

5Thread-1

7Thread-0

8Thread-0

6Thread-1

9Thread-0

7Thread-1

Thread-0线程运行结束!

8Thread-1

9Thread-1

Thread-1线程运行结束!

说明:

TestMitiThread1类通过实现Runnable接口,使得该类有了多线程类的特征。

run()方法是多线程程序的一个约定。

所有的多线程代码都在run方法里面。

Thread类实际上也是实现了Runnable接口的类。

在启动的多线程的时候,需要先通过Thread类的构造方法Thread(Runnabletarget)构造出对象,然后调用Thread对象的start()方法来运行多线程代码。

实际上所有的多线程代码都是通过运行Thread的start()方法来运行的。

因此,不管是扩展Thread类还是实现Runnable接口来实现多线程,最终还是通过Thread的对象的API来控制线程的,熟悉Thread类的API是进行多线程编程的基础。

五、读解Thread类API

staticintMAX_PRIORITY

线程可以具有的最高优先级。

staticintMIN_PRIORITY

线程可以具有的最低优先级。

staticintNORM_PRIORITY

分配给线程的默认优先级。

构造方法摘要

Thread(Runnabletarget)

分配新的Thread对象。

Thread(Stringname)

分配新的Thread对象。

方法摘要

staticThreadcurrentThread()

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 经管营销

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

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