Runnable 和Thread

线程(Runnable接口和Thread类)  

进程(process)----系统可以为一个程序同时创建多个进程,例如,同时打开两个记事本文件,每一个进程都有自己独立的一块内存空间和一组系统资源,即使同类进程之间也不会共享系统资源

线程(Thread)----就是比进程更小的运行单位,一个进程中可以包含多个线程,与进程不同的是,同类的多线程是共享一块内存空间和一组系统资源的,每一个程序至少拥有一个线程,成为主线程,当程序加载到内存时,启动主线程,要加载其它程序,程序就要使用Runnable接口和Thread类。

 

Runnable接口---Runnable接口只声明了一个run方法,run是线程执行的起点,即在创建并启动一个线程后,系统自动调用run方法,一个线程对象必须实现run的方法完成线程的所有活动。

 

Thread类----Thread类将Runnable接口中的run方法实现为空方法,它的声明格式为:

public class Thread extends Object implements Runnable

 

(1)构造方法

public Thread()

public Thread(String name)

public Thread(Runnable target)

public Thread(Runnable target,String name)

public Thread(ThreadGroup group,Runnable target)

public Thread(ThreadGroup group,String name)

public Thread(ThreadGroup group , Runnable target,String name)

(2)Thread类的静态方法:

public static Thread currentThread() //返回当前执行线程的引用对象

public static int activeCount()            //返回当前线程组中活动线程个数

public static enumerate(Thread[] tarray) //将当前线程总的活动线程拷贝到tarray数组中,包括

子线程

(3)Thread类的实例方法

public final String getName()           //返回线程名

public final void setName(String name) //设置线程的名字为那么

public void start() //启动已创建的线程对象

public final boolean isAlive() //返回线程是否启动的状态

public final ThreadGroup getThreadGroup() //返回当前线程所属的线程组名

public String toString()  //返回线程的字符串信息,包括名字,优先级和线程组

 

 

创建线程的两种方法:

(1)继承Thread类

由于Thread类的run()方法只是空方法,Thread1类必须覆盖run()方法。

在程序中,线程语句的顺序只决定了线程语句产生的顺序,线程产生后并不立即运行,而是和系统中的所有其它线程一起等待系统来执行它,这时所有线程执行的机会是均等的,所有这些线程的执行顺序由系统调度和控制,不由程序决定。

 

public class Thread1 extends Thread{
                     private int k=0;
                     public Thread1(String name,int k){
                         super(name);
                         this.k = k;
                     }
                    public void run(){
                        int i = k;
                        System.out.println();
                        System.out.println(getName()+": ");
                        while(i<50){
                           System.out.println(i+" ");
                           i+= 2;
                        }
                       System.out.println(getName()+" end!");
                   }
                   public static void main(String[] args) {
                       Thread1 t1 = new Thread1("Thread1",1);
                       Thread1 t2 = new Thread1("Thread2",2);
                       t1.start();
                       t2.start();
                       System.out.println("activeCount="+Thread.activeCount());
                   }

 

(2)实现Runnable接口 

public class Runnable1 implements Runnable{
                      int k=0;
                      public Runnable1(int k){
                          this.k = k;
                      }
                      public void run(){
                          int i = k;
                          System.out.println("");
                          while(i<500){
                              System.out.println(i+" ");
                               i += 2;
                          }
                      }
                      public static void main(String[] args) {
                          Runnable1 r1 = new Runnable1(1);
                          Runnable1 r2 = new Runnable1(2);
                         Thread t1 = new Thread(r1);
                         Thread t2 = new Thread(r2);
                         t2.start();
                         t1.start();
                     }
                }

Runnable1类只是实现Runnable的接口的一个类,它并不是Thread的子类。Runnable类的对象r1虽然带有run()方法的线程体,但其中没有start()方法,所以r1不是线程对象。由于Thread类的run()方法是空方法,所以在main()方法中,必须以Runnable1类的对象r1和r2作为目标对象,构造Thread类的线程t1和t2,之后通过t1.start()启动线程。

 

 

两种创建线程方法的比较

(1)直接继承线程Thread类

该方法编写简单,可以直接操作线程,适用于单重继承情况,因而不能再继承其他类

(2)实现Runnable接口

当一个线程已继承了另一个类时,就只能用实现Runnable接口的方法来创建线程,且便于保持程序风格的一致性


[第一需要弄清的问题]

  如同程序和进程的区别,要掌握多线程编程,第一要弄清的问题是:线程对象和线程的区别

  线程对象是可以产生线程的对象。比如在java平台中Thread对象,Runnable对象。线程,是指正在执行的一个指点令序列。在java平台上是指从一个线程对象的start()开始,运行run方法体中的那一段相对独立的过程。

  鉴于作者的水平,无法用更确切的词汇来描述它们的定义。但这两个有本质区别的概念请初学者细细体会,随着介绍的深入和例程分析的增加,就会慢慢明白它们所代表的真实含义。

  天下难事必始于易,天下大事必始于细。

  让我们先从最简单的"单线程"来入手:(1)带引号说明只是相对而言的单线程,(2)基于java。

    class BeginClass{

public static void main(String[] args){

for(int i=0;i<100;i++)

System.out.println("Hello,World!");

}

}

  如果我们成功编译了该java文件,然后在命令行上敲入:

  java BeginClass

  现在发生了什么呢?每一个java程序员,从他开始学习java的第一分钟里都会接触到这个问

  题,但是,你知道它到底发生发什么?

JVM进程被启动,在同一个JVM进程中,有且只有一个进程,就是它自己。然后在这个JVM环境中,所有程序的运行都是以线程来运行。JVM最先会产生一个主线程,由它来运行指定程序的入口点。在这个程序中,就是主线程从main方法开始运行。当main方法结束后,主线程运行完成。JVM进程也随之退出。

  我们看到的是一个主线程在运行main方法,这样的只有一个线程执行程序逻辑的流程我们称

  之为单线程。这是JVM提供给我们的单线程环境,事实上,JVM底层还至少有垃圾回收这样的后台线程以及其它非java线程,但这些线程对我们而言不可访问,我们只认为它是单线程的。

  主线程是JVM自己启动的,在这里它不是从线程对象产生的。在这个线程中,它运行了main方法这个指令序列。理解它,但它没有更多可以研究的内容。

  [接触多线程]

    class MyThread extends Thread{

public void run(){

System.out.println("Thread say:Hello,World!");

}

}



public class MoreThreads{

public static void main(String[] args){

new MyThread();

new MyThread().start();

System.out.println("Main say:Hello,World");

}

}

  执行这个程序,main方法第一行产生了一个线程对象,但并没有线程启动。

  main方法第二行产生了一个线程对象,并启动了一个线程。

  main方法第三行,产生并启动一个线程后,主线程自己也继续执行其它语句。

  我们先不研究Thread对象的具体内容,稍微来回想一下上面的两个概念,线程对象线程。在JAVA中,线程对象是JVM产生的一个普通的Object子类。而线程是CPU分配给这个对象的一个运行过程。我们说的这个线程在干什么,不是说一个线程对象在干什么,而是这个运行过程在干什么。如果一时想不明白,不要急,但你要记得它们不是一回事就行了。


现在我们来开始考察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实例)的多个线程


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值