一、概述
由于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类的构造方法被重载了八次,构造方法如下:- public Thread( );
- public Thread(Runnable target);
- public Thread(String name);
- 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);
- 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方法才能真正运行线程的代码
Thread
的子类
Thread
类的 run
方法
- 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();
- }
- }
- 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();
- }
- }