现在的位置: 首页 > 综合 > 正文

黑马程序员_线程基础

2013年10月09日 ⁄ 综合 ⁄ 共 4336字 ⁄ 字号 评论关闭

      一、概述  

    由于Java是纯面向对象语言,因此,Java的线程模型也是面向对象的。Java通过Thread类将线程所必须的功能都封装了起来。要想建立一个线程,必须要有一个线程执行函数,这个线程执行函数对应Thread类的run方法。Thread类还有一个start方法,这个方法负责建立线程。当调用start方法后,如果线程建立成功,并自动调用Thread类的run方法。因此,任何继承Thread的Java类都可以通过Thread类的start方法来建立线程。如果想运行自己的线程执行函数,那就要覆盖Thread类的run方法。注意:Java中的多线程是一种抢占机制而不是分时机制。抢占机制指的是有多个线程处于可运行状态,但是只允许一个线程在运行,他们通过竞争的方式抢占CPU。

在Java的线程模型中除了Thread类,还有一个标识某个Java类是否可作为线程类的接口Runnable,这个接口只有一个抽象方法run,也就是Java线程模型的线程执行函数。因此,一个线程类的唯一标准就是这个类是否实现了Runnable接口的run方法,也就是说,拥有线程执行函数的类就是线程类。

从上面可以看出,在Java中建立线程有两种方法,一种是继承Thread类,另一种是实现Runnable接口,并通过Thread和实现Runnable的类来建立线程,其实这两种方法从本质上说是一种方法,即都是通过Thread类来建立线程,并运行run方法的。但它们的大区别是通过继承Thread类来建立线程,虽然在实现起来更容易,但由于Java不支持多继承,因此,这个线程类如果继承了Thread,就不能再继承其他的类了,因此,Java线程模型提供了通过实现Runnable接口的方法来建立线程,这样线程类可以在必要的时候继承和业务有关的类,而不是Thread类。

二、介绍

从java的API开始,因为Runnable是顶层接口,所以先说一下它。API中是这样说明的:Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。此外,Runnable 为非 Thread 子类的类提供了一种激活方式。通过实例化某个 Thread 实例并将自身作为运行目标,就可以运行实现 Runnable 的类而无需创建 Thread 的子类。大多数情况下,如果只想重写 run() 方法,而不重写其他 Thread 方法,那么应使用 Runnable 接口。这很重要,因为除非程序员打算修改或增强类的基本行为,否则不应为该类创建子类。 

通过说明我们知道,要想使用Runnable来创建线程,实现Runnable接口的类必须使用Thread类的实例才能创建线程。通过Runnable接口创建线程分为三步:

1. 将实现Runnable接口的类实例化。

2. 建立一个Thread对象,并将第一步实例化后的对象作为参数传入Thread类的构造方法。

3. 最后通过Thread类的start方法建立线程。

下面的代码演示了如何使用Runnable接口来创建线程:

package com.debug.java;

public class RunnableDemo implements Runnable {

	/**

	 * @category 第一步:将实现Runnable接口的类实例化。

	 */

	public static void main(String[] args) {

		RunnableDemo r = new RunnableDemo();// 第二步:实例化RunnableDemo,并将其作为Thread的参数

		Thread thread1 = new Thread(r);

		Thread thread2 = new Thread(r, "thread2");

		thread1.start();// 第三步:开始线程

		thread2.start();

	}

	@Override

	public void run() {

		System.out.println(Thread.currentThread().getName());

	}

}

输出:Thread-0

            thread2

介绍完Runnable接口,再来看一下Thread类,它继承自Runnable接口。Thread类的构造方法被重载了八次,构造方法如下:

  1. public Thread( );  
  2. public Thread(Runnable target);  
  3. public Thread(String name);  
  4. public Thread(Runnable target, String name);  
  5. public Thread(ThreadGroup group, Runnable target);  
  6. public Thread(ThreadGroup group, String name);  
  7. public Thread(ThreadGroup group, Runnable target, String name);  
  8. public Thread(ThreadGroup group, Runnable target, String name, long stackSize); 

Runnable target:

实现了Runnable接口的类的实例。要注意的是Thread类也实现了Runnable接口,因此,从Thread类继承的类的实例也可以作为target传入这个构造方法。

String name:

线程的名子。这个名子可以在建立Thread实例后通过Thread类的setName方法设置。如果不设置线程的名子,线程就使用默认的线程名:Thread-N,N是线程建立的顺序,是一个不重复的正整数。

ThreadGroup group:

当前建立的线程所属的线程组。如果不指定线程组,所有的线程都被加到一个默认的线程组中。

long stackSize:

线程栈的大小,这个值一般是CPU页面的整数倍。如x86的页面大小是4KB。在x86平台下,默认的线程栈大小是12KB。

一个普通的Java类只要从Thread类继承,就可以成为一个线程类。并可通过Thread类的start方法来执行线程代码。虽然Thread类的子类可以直接实例化,但在子类中必须要覆盖Thread类的run方法才能真正运行线程的代码

它创建线程的方法是:
1、将类声明为 Thread 的子类
2、该子类应重写 Thread 类的 run 方法
3、接下来可以分配并启动该子类的实例
下面的代码演示了如何使用Runnable接口来创建线程:
package com.debug.java;
public class ThreadDemo extends Thread{
	/*
	 * 重写run方法
	 */
	@Override
	public void run() {
		super.run();
		System.out.println(Thread.currentThread().getName());
	}
	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("main线程:"+Thread.currentThread().getName()); 
		ThreadDemo thread1=new ThreadDemo();
		ThreadDemo thread2=new ThreadDemo();
		thread1.start();
		thread2.start();
	}
}
输出:main线程:main
      Thread-0
      Thread-1    由于在建立线程时并未指定线程名,因此,所输出的线程名是系统的默认值,也就是Thread-n的形式
注意:任何一个Java程序都必须有一个主线程。一般这个主线程的名子为main。只有在程序中建立另外的线程,才能算是真正的多线程程序。也就是说,多线程程序必须拥有一个以上的线程。Thread类的start方法不能多次调用,如不能调用两次thread1.start()方法。否则会抛出一个IllegalThreadStateException异常。
Thread类有一个重载构造方法可以设置线程名。除了使用构造方法在建立线程时设置线程名,还可以使用Thread类的setName方法修改线程名。要想通过Thread类的构造方法来设置线程名,必须在Thread的子类中使用Thread类的public Thread(String name)构造方法,因此,必须在Thread的子类中也添加一个用于传入线程名的构造方法。下面的代码给出了一个设置线程名的例子:
package com.debug.java;

public class ThreadDemo extends Thread {

	private String str;

	public ThreadDemo(String str) {

		this.str = str;
	}

	public ThreadDemo(String str, String threadName) {
		super(threadName);
		this.str = str;
	}

	/*
	 * 重写run方法
	 */
	@Override
	public void run() {
		super.run();
		System.out.println(Thread.currentThread().getName());
	}

	/**
	 * @param args
	 */
	public static void main(String[] args) {
		System.out.println("main线程:" + Thread.currentThread().getName());
		ThreadDemo thread1 = new ThreadDemo("thread1");
		thread1.setName("thread1");
		ThreadDemo thread2 = new ThreadDemo("thread2", "thread2");
		ThreadDemo thread3 = new ThreadDemo("thread2", "thread2");
		thread1.start();
		thread2.start();
		thread3.start();
	}

}
输出:main线程:main
thread1
thread2
thread2
注意:在调用start方法前后都可以使用setName设置线程名,但在调用start方法后使用setName修改线程名,会产生不确定性,也就是说可能在run方法执行完后才会执行setName。如果在run方法中要使用线程名,就会出现虽然调用了setName方法,但线程名却未修改的现象。

抱歉!评论已关闭.