一、理解多线程
多线程是这样一种机制,它允许在程序中并发执行多个指令流,每个指令流都称为一个线程,彼此间互相独立。
线程又称为轻量级进程,它和进程一样拥有独立的执行控制,由操作系统负责调度,区别在于线程没有独立的存储空间,而是和所属进程中的其它线程共享一个存储空间,这使得线程间的通信远较进程简单。
多个线程的执行是并发的,也就是在逻辑上“同时”,而不管是否是物理上的“同时”。如果系统只有一个CPU,那么真正的“同时”是不可能的,但是由于CPU的速度非常快,用户感觉不到其中的区别,因此我们也不用关心它,只需要设想各个线程是同时执行即可。
多线程和传统的单线程在程序设计上最大的区别在于,由于各个线程的控制流彼此独立,使得各个线程之间的代码是乱序执行的,由此带来的线程调度,同步等问题,将在以后探讨。
二、在Java中实现多线程
我们不妨设想,为了创建一个新的线程,我们需要做些什么?很显然,我们必须指明这个线程所要执行的代码,而这就是在Java中实现多线程我们所需要做的一切!
真是神奇!Java是如何做到这一点的?通过类!作为一个完全面向对象的语言,Java提供了类java.lang.Thread来方便多线程编程,这个类提供了大量的方法来方便我们控制自己的各个线程,我们以后的讨论都将围绕这个类进行。
那么如何提供给 Java 我们要线程执行的代码呢?让我们来看一看 Thread 类。Thread 类最重要的方法是run(),它为Thread类的方法start()所调用,提供我们的线程所要执行的代码。为了指定我们自己的代码,只需要覆盖它!
方法一:继承 Thread 类,覆盖方法 run(),我们在创建的 Thread 类的子类中重写 run() ,加入线程所要执行的代码即可。下面是一个例子:
public class MyThread extends Thread{
int count= 1, number;
public MyThread(int num){
number = num;
System.out.println
("创建线程 " + number);
}
public void run() {
while(true) {
System.out.println
("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[])
{
for(int i = 0;i 〈 5; i++) new MyThread(i+1).start();
}
}
这种方法简单明了,符合大家的习惯,但是,它也有一个很大的缺点,那就是如果我们的类已经从一个类继承(如小程序必须继承自 Applet 类),则无法再继承 Thread 类,这时如果我们又不想建立一个新的类,应该怎么办呢?
我们不妨来探索一种新的方法:我们不创建Thread类的子类,而是直接使用它,那么我们只能将我们的方法作为参数传递给 Thread 类的实例,有点类似回调函数。但是 Java 没有指针,我们只能传递一个包含这个方法的类的实例。
那么如何限制这个类必须包含这一方法呢?当然是使用接口!(虽然抽象类也可满足,但是需要继承,而我们之所以要采用这种新方法,不就是为了避免继承带来的限制吗?)
Java 提供了接口 java.lang.Runnable 来支持这种方法。
方法二:实现 Runnable 接口
Runnable接口只有一个方法run(),我们声明自己的类实现Runnable接口并提供这一方法,将我们的线程代码写入其中,就完成了这一部分的任务。但是Runnable接口并没有任何对线程的支持,我们还必须创建Thread类的实例,这一点通过Thread类的构造函数 public Thread(Runnable target);来实现。下面是一个例子:
public class MyThread implements Runnable
{
int count= 1, number;
public MyThread(int num)
{
number = num;
System.out.println("创建线程 " + number);
}
public void run()
{
while(true)
{
System.out.println
("线程 " + number + ":计数 " + count);
if(++count== 6) return;
}
}
public static void main(String args[])
{
for(int i = 0; i 〈 5;i++) new Thread(new MyThread(i+1)).start();
}
}
严格地说,创建Thread子类的实例也是可行的,但是必须注意的是,该子类必须没有覆盖 Thread 类的 run 方法,否则该线程执行的将是子类的 run 方法,而不是我们用以实现Runnable 接口的类的 run 方法,对此大家不妨试验一下。
使用 Runnable 接口来实现多线程使得我们能够在一个类中包容所有的代码,有利于封装,它的缺点在于,我们只能使用一套代码,若想创建多个线程并使各个线程执行不同的代码,则仍必须额外创建类,如果这样的话,在大多数情况下也许还不如直接用多个类分别继承 Thread 来得紧凑。
综上所述,两种方法各有千秋,大家可以灵活运用。
下面让我们一起来研究一下多线程使用中的一些问题。
三、线程的四种状态
1. 新状态:线程已被创建但尚未执行(start() 尚未被调用)。
2. 可执行状态:线程可以执行,虽然不一定正在执行。CPU 时间随时可能被分配给该线程,从而使得它执行。
3. 死亡状态:正常情况下 run() 返回使得线程死亡。调用 stop()或 destroy() 亦有同样效果,但是不被推荐,前者会产生异常,后者是强制终止,不会释放锁。
4. 阻塞状态:线程不会被分配 CPU 时间,无法执行。
四、线程的优先级
线程的优先级代表该线程的重要程度,当有多个线程同时处于可执行状态并等待获得 CPU 时间时,线程调度系统根据各个线程的优先级来决定给谁分配 CPU 时间,优先级高的线程有更大的机会获得 CPU 时间,优先级低的线程也不是没有机会,只是机会要小一些罢了。
你可以调用 Thread 类的方法 getPriority() 和 setPriority()来存取线程的优先级,线程的优先级界于1(MIN_PRIORITY)和10(MAX_PRIORITY)之间,缺省是5(NORM_PRIORITY)。
多线程是Java语言的一大特性,多线程就是同时存在N个执行体,按几条不同的执行线索共同工作的情况。程序,进程,线程,可以从不同的角度去理解。程序就是一段静态的代码,可以理解成一组计算机命令的集合.进行就是这个程序一次动态的执行过程,从代码的加载到执行完毕的一个过程。线程是一个比进程小的单位,一个进程再执行的过程中可以产生多个线程,每个线程也是由生产到销毁,可以理解成是进行的子集。我个人用一个觉得还算恰当的比喻来比喻三者。QQ客户端就是一个程序,登陆一个QQ就是开始了这个程序的一个进程,再QQ上发送消息给好友就貌似这个进程中的一个线程。不知道这样比喻恰当否?
线程也是有状态和声明周期的,每个Java程序都会有一个缺省的主线程,对于应用程序applcation来说main方法就是一个主线程.Java语言使用的是Thread类及其子类的对象来表示线程的.创建一个新的线程的生命周期如下状态:
1) 新建:当一个Thread类或者其子类的对象被声明并创建时,新的线程对象处于新建状态,此时它已经有了相应的内存空间和其他资源.
2) 就绪:处于新建状态的线程被启动后,将进入线程队列排队等待CUP服务,这个时候具备了运行的条件,一旦轮到CPU的时候,就可以脱离创建它的主线程独立开始自己的生命周期.
3) 运行:就绪的线程被调度并获得CUP的处理边进入了运行状态,每一个Thread类及其子类的对象都有一个重要的run()方法,当线程对象被调度执行的时候,它将自动调用本对象的run()方法,从第一句代码开始执行。所以说对线程的操作应该写到run()方法中.
4) 阻塞:一个正在执行的线程如果再某种情况下不能执行了.进入阻塞状态,这个时候它不能进入排队状态,只有引起了阻塞的原因消失的时候,线程才可以继续进入排队状态等待CUP处理。
5) 死亡:处于死亡状态的线程不具有继续执行的能力,线程死亡主要的原因是正常运行的线程完成了全部工作,即执行完了run()方法,另外就是被提前强制的终止了。
线程的调度也是有优先级别的,就是说同样的排列优先级高可以提前被CPU处理,主要分三个级别,高中低.分别代表的数字是10.5.1分别有三个常量代表不可以被改变。最小优先级的常量是MIN_PRIORITY,普通的优先级的常量是NORM_PRIORITY,最高的优先级的常量是MAX_PRIORITY一般主线程的优先级是普通,另外可以通过Thread类的setPriority(int a)方法来修改系统自动设置的线程优先级。
Java中编程实现多线程应有两种途径,一种是创建自己的线程子类,另外是实现一个接口Runnable。无论是那种途径最终读需要使用Thread类及其方法。Thread类有两种构造方法,public Thread()用来创建一个线程对象。public Thread(Runnable r)创建线程对象,参数r成为被创建的目标对象。这个目标必须实现Runnbale接口,给出该接口的run()方法的方法体,再方法体中操作.用两个构造方法创建完的线程就是一个新建的状态,等待处理.然后启动线程的start()方法,启动线程对象,线程进入排队状态也就是就绪状态.然后线程操作run()方法这个方法里的内容是被系统处理的内容.如果想使线程进入休眠状态可以调用sleep()方法,可以给一个参数,代表休眠的毫秒.如果给两个参数代表那秒。终止线程用yield()方法来完成.判断线程是否销毁可以用idAlive()方法判断线程是否活着。Runnable接口只有一个方法run()方法,我们实现这个接口把要操作的代码写到这个方法中,然后再把实现了整个接口的类的实例传给Therad类的构造方法即可操作线程。
线程同步是一个再处理线程的时候需要注意的问题,同步方法要用synchronized关键字类修饰,被这个关键字修饰后,当一个线程A使用这个方法后,其它线程想使用这个方法就必须等待,知道线程A使用完该方法后方可使用.下面我写一个例子来说明线程同步,这个例子有两个线程会计和出纳,他们共同有一个账本.他们俩都可以存取方法对账本进行访问,会计使用存取方法的时候写入存钱的记录,出纳则写入取钱的记录。因此会计使用账本的时候出纳被禁止使用,反之也是如此。就是一个人使用另外一个人必须等待。下面我通过一个小程序Applet来实现这个事:
import Java.applet.*;
import Java.awt.*;
import Java.awt.event.*;
public class MyThread extends Applet implements Runnable {
int money = 100;
TextArea text1 = null;
TextArea text2 = null;
Thread Kuaiji = null;
Thread Chuna = null;
public void init() {
Kuaiji = new Thread(this);
Chuna = new Thread(this);
text1 = new TextArea(20,8);
text2 = new TextArea(20,8);
add(text1);
add(text2);
}
public void start() {
Kuaiji.start();
Chuna.start();
}
public synchronized void Cunqu(int number) {
if(Thread.currentThread() == Kuaiji) {
for(int i=1;i<=3;i++) {
money = money + number;
try {Thread.sleep(1000);}
catch(Exception e){}
text1.append("\n"+money);
}
}
else if(Thread.currentThread() == Chuna) {
for(int i=1;i<=2;i++) {
money = money - number/2;
try {Thread.sleep(1000);}
catch(Exception e){}
text2.append("\n"+money);
}
}
}
public void run() {
if(Thread.currentThread()==Kuaiji || Thread.currentThread()==Chuna) {
for(int i=1;i<=3;i++) {
Cunqu(30);
}
}
}
}
当一个线程使用同步方法中的某个变量,而此变量又需要其他线程修改后才能符合本线程的需要,那么可以再同步方法中使用wait()方法,使本线程等待,并允许其他线程使用这个同步方法.用notfyAll()方法同志所有的由于使用这个同步方法的处于等待的线程结束等待进入同步方法中运行,如果使使用notfy()就是单独同志一个线程进行同步方法进行活动.简单的理解就是wait()方法让线程等待,notfy()当一个线程运行,notfyAll()让全部线程运行。虽然Java支持多线程.一般线程不需要我们自己处理.但是也是需要了解和掌握的。再日后的项目中获取会根据不同情况,有不同的需求。
分享到:
相关推荐
电子书相关:包含4个有关JAVA线程的电子书(几乎涵盖全部有关线程的书籍) OReilly.Java.Threads.3rd.Edition.Sep.2004.eBook-DDU Java Thread Programming (Sams) java线程第二版中英文 java线程第二版中英文 ...
Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解Java线程讲解
Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程Java线程
Java线程Java线程Java线程Java线程Java线程Java线程
Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-优先级 ...
此书是讲java线程技术的书籍,下载的小伙伴们看清楚 啊!
java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程java 线程
Java线程模块Java线程之秒表新手学习Java线程模块时,利用Java中设置线程的暂停间隔,做的简易秒表
Java线程:概念与原理 Java线程:创建与启动 Java线程:线程栈模型与线程的变量 Java线程:线程状态的转换 Java线程:线程的同步与锁 Java线程:线程的交互 Java线程:线程的调度-休眠 Java线程:线程的调度-...
java 线程Dump 分析工具: Java的TDA线程转储分析器是一个用于分析Sun Java VM生成的线程转储和堆信息的小型Swing GUI(目前用1.4测试)。它从提供的日志文件中解析线程转储和类直方图。它提供关于发现的线程转储的...
java线程同步java线程同步java线程同步
java线程 线程 教程 java线程教程 java线程学习资料 本教程有什么内容? 本教程研究了线程的基础知识— 线程是什么、线程为什么有用以及怎么开始编写使用线程的简单 程序。 我们还将研究更复杂的、使用线程的应用...
java线程第三版源代码, jthreads3rd.src
Java的线程和Java AppletJava的线程和Java AppletJava的线程和Java AppletJava的线程和Java AppletJava的线程和Java Applet
Java线程:概念与原理 2 一、操作系统中线程和进程的概念 2 二、Java中的线程 3 三、Java中关于线程的名词解释 3 四、线程的状态转换和生命周期 4 Java线程:创建与启动 7 Java线程:线程名称的设定及获取 10 Java...
java线程.pdf java 学习java
java 线程 新手java 线程 新手java 线程 新手java 线程 新手
分析java线程日志的工具,使用jstack把java线程日志dump下来,然后上传到该工具,就可以查看线程阻塞情况等信息。
Java 模拟线程并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发 Java, 模拟线程并发,线程,并发
Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式上传文件Java多线程设计模式...