`
阅读更多

多线程编程——基础篇 (二)


时间:2006-08-16
作者:axman
浏览次数: 3207
本文关键字:Java, 多线程, 线程, 线程对象, 单线程, go deep into java

start

public void start()
使该线程开始执行;Java 虚拟机调用该线程的 run 方法。

结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。

多次启动一个线程是非法的。特别是当线程已经结束执行后,不能再重新启动。

 

抛出:
IllegalThreadStateException - 如果线程已经启动。
另请参见:
run(), stop()

<!---->

run

public void run()
如果该线程是使用独立的 Runnable 运行对象构造的,则调用该 Runnable 对象的 run 方法;否则,该方法不执行任何操作并返回。

Thread 的子类应该重写该方法。

 

指定者:
接口 Runnable 中的 run
另请参见:
start(), stop(), Thread(java.lang.ThreadGroup, java.lang.Runnable, java.lang.String), Runnable.run()

java.lang
接口 Runnable

所有已知实现类:
AsyncBoxView.ChildState, FutureTask, RenderableImageProducer, Thread, TimerTask

 

public interface Runnable
 

Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。

设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,Thread 类实现了 Runnable。激活的意思是说某个线程已启动并且尚未停止。

此外,Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个 Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。

[JAVA线程对象]

  现在我们来开始考察JAVA中线程对象。

  在JAVA中,要开始一个线程,有两种方式。一是直接调用Thread实例的start()方法,二是
将Runable实例传给一个Thread实例然后调用它的start()方法。

  在前面已经说过,线程对象和线程是两个完全不同的概念。这里我们再次深入一下,生成一个线程的实例,并不代表启动了线程。而启动线程是说在某个线程对象上启动了该实例对应的线程,当该线程结束后,并不会就立即消失。

  对于从很多书籍上可以看到的基础知识我就不用多说了。既然是基础知识,我也着重于从普通文档上读不到的内容。所以本节我重点要说的是两种线程对象产生线程方式的区别。

class MyThread extends Thread{
  public int x = 0;
 
  public void run(){
   
    for(int i=0;i<100;i++){
      try{
        Thread.sleep(10);
      }catch(Exception e){}
      System.out.println(x++);
     
    }
  }
}

  如果我们生成MyThread的一个实例,然后调用它的start()方法,那么就产生了这个实例对应的线程:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
  }
}

  不用说,最终会打印出0到99,现在我们稍微玩一点花样:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    System.out.println(101);
  }
}

  也不用说,在基础篇(一)中我们知道由于单CPU的原因,一般会先打印101,然后打印0到99。不过我们可以控制线程让它按我们的意思来运行:

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    mt.join();
    System.out.println(101);
  }
}

  好了,我们终于看到,mt实例对应的线程(假如我有时说mt线程请你不要怪我,不过我尽量不这么说)。在运行完成后,主线程才打印101。因为我们让当前线程(这里是主线程)等待mt线程的运行结束。"在线程对象a上调用join()方法,就是让当前正在执行的线程等待线程对象a对应的线程运行完成后才继续运行。" 请大家一定要深刻理解并熟记这句话,而我这里引出这个知识点的目的是为了让你继续看下面的例子:

 

public class Test {
  public static void main(String[] args) throws Exception{
    MyThread mt = new MyThread();
    mt.start();
    mt.join();
    Thread.sleep(3000);
    mt.start();
  }
}

  当线程对象mt运行完成后,我们让主线程休息一下,然后我们再次在这个线程对象上启动线程。结果我们看到:

  Exception in thread "main" java.lang.IllegalThreadStateException

  也就是这种线程对象一时运行一次完成后,它就再也不能运行第二次了。我们可以看一下它有具体实现:

    public synchronized void start() {
        if (started)
            throw new IllegalThreadStateException();
        started = true;
        group.add(this);
        start0();
    }

  一个Thread的实例一旦调用start()方法,这个实例的started标记就标记为true,事实中不管这个线程后来有没有执行到底,只要调用了一次start()就再也没有机会运行了,这意味着:
[通过Thread实例的start(),一个Thread的实例只能产生一个线程]

  那么如果要在一个实例上产生多个线程(也就是我们常说的线程池),我们应该如何做呢?这就是Runnable接口给我们带来的伟大的功能。

 

class R implements Runnable{
  private int x = 0;
  public void run(){

    for(int i=0;i<100;i++){
      try{
        Thread.sleep(10);
      }catch(Exception e){}
      System.out.println(x++);

    }
  }
}

  
正如它的名字一样,Runnable的实例是可运行的,但它自己并不能直接运行,它需要被Thread对象来包装才行运行:

public class Test {
  public static void main(String[] args) throws Exception{
    new Thread(new R()).start();
  }
}

  当然这个结果和mt.start()没有什么区别。但如果我们把一个Runnable实例给Thread对象多次包装,我们就可以看到它们实际是在同一实例上启动线程:

public class Test {
  public static void main(String[] args) throws Exception{
    R r = new R();
    for(int i=0;i<10;i++)
      new Thread(r).start();
  }
}

  x是实例对象,但结果是x被加到了999,说明这10个线程是在同一个r对象上运行的。请大家注意,因为这个例子是在单CPU上运行的,所以没有对多个线程同时操作共同的对象进行同步。这里是为了说明的方便而简化了同步,而真正的环境中你无法预知程序会在什么环境下运行,所以一定要考虑同步。

 

到这里我们做一个完整的例子来说明线程产生的方式不同而生成的线程的区别:

package debug;

import java.io.*;
import java.lang.Thread;


class MyThread extends Thread{
  public int x = 0;

  public void run(){
    System.out.println(++x);
  }
}

class R implements Runnable{
  private int x = 0;
  public void run(){
    System.out.println(++x);
  }
}

public class Test {
  public static void main(String[] args) throws Exception{
   
    for(int i=0;i<10;i++){
      Thread t = new MyThread();
      t.start();
    }
    Thread.sleep(10000);//让上面的线程运行完成
    R r = new R();
    for(int i=0;i<10;i++){
      Thread t = new Thread(r);
      t.start();
    }
  }
}

  上面10个线程对象产生的10个线程运行时打印了10次1。下面10个线程对象产生的10个线程运行时打印了1到10。我们把下面的10个线程称为同一实例(Runnable实例)的多个线程。

  下节我们将研究线程对象方法,还是那句话,一般文档中可以读到的内容我不会介绍太多
请大家自己了解。

转载自dev2dev网友axman的go deep into java专栏。

分享到:
评论

相关推荐

    多线程的学习资料(对初学者特别有用)!

    在学习多线程的过程中,理解并发模型和线程安全是非常关键的。线程安全意味着在多线程环境下,一个类或方法的行为是可预测且正确的。为了实现线程安全,我们需要使用原子操作、 volatile关键字、ThreadLocal变量或者...

    很好的一篇学习多线程的资源

    对于想要深入学习多线程的读者而言,《很好的一篇学习多线程的资源》这篇文章提供了丰富的实例和实用技巧,非常适合初学者入门以及进阶学习。 通过上述介绍,我们可以看到多线程技术在提升程序性能、优化用户体验...

    C# 打字游戏(学习多线程必看)

    本文将深入探讨“C#打字游戏”这个项目,它不仅是一个有趣的小游戏,也是学习多线程编程的理想实例。通过分析这个项目,我们可以了解多线程在实际开发中的应用和重要性。 首先,多线程是现代计算机编程中的关键概念...

    系统化学习多线程1

    【系统化学习多线程1】的概述主要涵盖了线程和进程的基本概念,以及它们在操作系统中的作用。线程和进程是操作系统中的核心概念,它们决定了程序如何在计算机上并发执行。 **1. 进程与线程的区别** - **进程...

    攻克学习多线程时碰到的难题

    在学习多线程的过程中,经常会遇到各种挑战,如线程同步、死锁、资源竞争等问题。本文将针对这些难题进行详细解析,旨在帮助初学者更好地理解和掌握多线程技术。 首先,我们来看线程的创建。Java 中有两种创建线程...

    02 绝对不仅仅是为了面试—我们为什么需要学习多线程.pdf

    学习多线程不仅是面试的必备技能,更是提升软件性能和解决实际问题的关键。在Java并发编程学习宝典中,作者强调了多线程学习的实用性和面试中的重要性。 1. **面试官考查多线程的原因**: - **技术深度**:尽管...

    学习多线程之一:线程通信--利用事件对象.zip_线程通信

    在"学习多线程之一:线程通信--利用事件对象.zip"这个压缩包中,包含的"www.pudn.com.txt"可能是对教程的介绍或引用资料,而"Thread1"可能是一个源代码文件,展示了如何使用事件对象进行线程通信的实际示例。...

    C#摇奖机(学习多线程的看)

    这个摇奖机程序不仅是一个趣味性的应用,也是一个学习多线程技术的良好起点。 首先,让我们深入理解多线程的概念。在单线程环境中,程序按照一定的顺序执行任务,而在多线程环境下,程序可以同时处理多个任务,提高...

    C#多线程教程,学习多线程的例子

    学习C#多线程不仅需要理解理论知识,更重要的是实践。通过编写并运行实际的多线程程序,你可以更好地理解线程的创建、执行、同步以及可能出现的问题。本教程中的例子将为你提供宝贵的实践经验,帮助你成为一个熟练的...

    C#多线程学习,学习多线程好资料。

    ### C#多线程学习详解 #### 一、多线程的基本概念 ##### 1. 什么是进程? 进程是程序的一次动态执行过程,它包含了正在运行的程序所需的内存、系统资源以及执行上下文。简单来说,当你启动一个程序时,操作系统会...

    多线程编程指南_学习多线程编程的宝典

    在IT领域,多线程编程是一项关键技能,尤其是在性能要求高的应用中,如服务器软件、游戏引擎或实时数据处理系统。C++作为一种强大的系统级编程语言,提供了丰富的工具和技术来支持多线程编程。本指南将深入探讨多...

    java学习多线程处理

    Java学习中的多线程处理是程序设计中的重要概念,它涉及到进程、线程以及它们的交互。进程是应用程序的执行实例,具备动态性、并发性和独立性,这意味着它可以同时进行多个任务。线程则是在进程中执行的一个单元,是...

    多线程.zip

    源码是学习多线程的重要实践工具,它可以帮助我们直观地看到线程如何在程序中工作,理解并发执行的机制,以及如何处理线程间的交互和同步问题。 在C#中,多线程主要通过System.Threading命名空间中的类实现,如...

    工作中学习多线程-一起进步

    在IT行业中,多线程是程序设计中的一个重要概念,特别是在Python这样的高级编程语言中。多线程允许程序同时执行多个任务,从而提高了程序的效率和...通过不断学习和实践,我们可以更好地掌握多线程技术,提升工作效率。

    多线程多任务下载软件.zip易语言项目例子源码下载

    这个易语言项目实例可以作为学习多线程和多任务下载的起点,通过阅读和分析源码,可以深入理解这些概念在实际应用中的实现方式。对于个人学习、学生毕业设计或小型团队项目来说,这样的例子是非常有价值的参考资料。

    VC中多线程10个详细例程

    为了解决这个问题,我们需要学习多线程编程。 二、多线程概述 多线程编程是指在一个进程中创建多个线程,每个线程都是进程内部的一个执行单元。主执行线程是进程内部的第一个线程,由系统自动创建。用户可以根据...

    多线程操作窗口源码2012813

    多线程操作窗口源码 功能介绍: 多线程是一种提高程序运行效率和性能的常用技术。 随着我们学习工作的深入,在编程中或多或少会涉及到 ... 学习多线程的可以下载参考下。 注意: 开发环境为Visual Studio 2010

    C++多线程 最简易的多线程程序

    在标签“建议模拟多线程”中,可能是指通过模拟并发情况来学习多线程。这可以通过编写多个简单的线程函数,观察它们如何相互交互和竞争资源来实现。 在实际开发中,多线程应用可能会涉及到更复杂的问题,如死锁、...

    易语言简单多线程写法源码

    总之,易语言简单多线程写法源码是学习多线程编程的一个好起点,特别是对于初学者,它可以帮助理解多线程的基本原理和实践方法。通过实际操作和调试这些源码,我们可以更好地掌握易语言中的多线程编程技术,并为将来...

Global site tag (gtag.js) - Google Analytics