在 Java 中有两种方式可以实现多线程,一种是继承 Thread 类,一种是实现 Runnable 接口。
Thread 类
Thread 类是在 java.lang 包中定义的。一个类只要继承了 Thread 类同时覆写了其中的 run() 方法就可以实现多线程操作了。但是一个类只能继承一个父类,这是此方法的局限,下面看例子:
package org.thread.demo;
class MyThread extends Thread {
private String name;
public MyThread(String name) {
super();
this.name = name;
}
public void run() {
for(int i=0; i < 10; i++) {
System.out.println("线程开始:"+this.name+",i="+i);
}
}
}
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程a");
MyThread mt2 = new MyThread("线程b");
mt1.run();
mt2.run();
}
}
但是,此时的结果是很有规律的,先执行第一个对象,然后执行第二个对象,并没有体现出多线程并发执行的效果,看下图的结果:

这是为什么呢?查阅 JDK 文档,我们找到了 start() 方法的原方法声明,一旦执行 start() 方法,就会调用 run() 方法,于是我们把上面的程序稍作修改,通过调用 start() 方法来启动线程:
package org.thread.demo;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程a");
MyThread mt2 = new MyThread("线程b");
mt1.start();
mt2.start();
}
};
再来看看这次修改后的执行结果:

显然,使用 start() 方法可以实现 Java 的多线程机制。那么为什么要使用 start() 方法来启动多线程呢? start() 方法又是怎么实现的?它的内部原理是什么?带着这些问题,我们来一步一步剖析Java 的源代码。
Thread.java
/**
* Causes this thread to begin execution; the Java Virtual Machine
* calls the <code>run</code> method of this thread.
* <p>
* The result is that two threads are running concurrently: the
* current thread (which returns from the call to the
* <code>start</code> method) and the other thread (which executes its
* <code>run</code> method).
* <p>
* It is never legal to start a thread more than once.
* In particular, a thread may not be restarted once it has completed
* execution.
*
* @exception IllegalThreadStateException if the thread was already
* started.
* @see #run()
* @see #stop()
*/
public synchronized void start() {
/**
* This method is not invoked for the main method thread or "system"
* group threads created/set up by the VM. Any new functionality added
* to this method in the future may have to also be added to the VM.
*
* A zero status value corresponds to state "NEW".
*/
if (threadStatus != 0)
throw new IllegalThreadStateException();
/* Notify the group that this thread is about to be started
* so that it can be added to the group's list of threads
* and the group's unstarted count can be decremented. */
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
/* do nothing. If start0 threw a Throwable then
it will be passed up the call stack */
}
}
}
private native void start0();可以发现,start() 方法中调用了 start0() 方法,其中,使用了 native 关键字来声明 start0() 方法。
什么是 Native Method?
简单地说,一个 Native Method 就是一个 Java 调用非 Java 代码的接口。Native Method 是这样一个 Java 的方法:该方法的实现由非 Java 语言实现,一般是 C 。这个特征并非 Java 所特有,很多其它的编程语言都有这一机制,比如在 C++ 中,你可以用extern "c" 告知 C++ 编译器去调用一个 C 的函数。因为 Native Method 方法,使得 start0() 可以直接调用系统底层的许多函数,这就是所谓的 JNI 技术(Java Native Interface)。
Runnable 接口
在实际开发中,一般多线程的操作很少使用 Thread 类,而是通过实现 Runnable 接口来完成。
public interface Runnable {
public void run();
}
例子:
package org.runnable.demo;
class MyThread implements Runnable {
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("线程开始:" + this.name+",i="+i);
}
}
};
但是在使用 Runnable 定义的子类时没有发现 start() 方法,只有在 Thread 类中才有。此时观察 Thread 类,有一个构造方法:
public Thread(Runnable target)
此构造方法接受 Runnable 的子类实例,也就是说可以通过 Thread 类来启动 Runnable 从而实现多线程操作。(start() 方法可以协调系统的底层资源)
package org.runnable.demo;
import org.runnable.demo.MyThread;
public class ThreadDemo01 {
public static void main(String[] args) {
MyThread mt1 = new MyThread("线程a");
MyThread mt2 = new MyThread("线程b");
new Thread(mt1).start();
new Thread(mt2).start();
}
}两种实现方式的区别和联系:
在程序开发中只要是多线程肯定永远以实现 Runnable 接口为主,因为实现 Runnable 接口相比继承 Thread 类有如下好处:
· 避免点继承的局限,一个类可以继承多个接口。
· 适用于资源的共享
以卖票程序为例,通过 Thread 类完成:
package org.demo.dff;
class MyThread extends Thread {
private int ticket=10;
public void run() {
for(int i = 0; i < 20; i++){
if (this.ticket > 0) {
System.out.println("賣票:ticket" + this.ticket--);
}
}
}
};
下面通过三个线程对象,同时卖票:
package org.demo.dff;
public class ThreadTicket {
public static void main(String[] args) {
MyThread mt1 = new MyThread();
MyThread mt2 = new MyThread();
MyThread mt3 = new MyThread();
mt1.start(); //每个线程都各卖了10张,共卖了30张票
mt2.start(); //但实际只有10张票,每个线程都卖自己的票
mt3.start(); //没有达到资源共享
}
}
如果使用 Runnable 就可以实现资源共享,看下面的例子:
package org.demo.runnable;
class MyThread implements Runnable {
private int ticket = 10;
public void run() {
for (int i = 0; i < 20; i++) {
if (this.ticket > 0) {
System.out.println("賣票:ticket" + this.ticket--);
}
}
}
}
package org.demo.runnable;
public class RunnableTicket {
public static void main(String[] args) {
MyThread mt = new MyThread();
new Thread(mt).start(); //同一个mt,但是在Thread中就不可以,如果用同一
new Thread(mt).start(); //个实例化对象mt,就会出现异常
new Thread(mt).start();
}
};
现在,虽然程序中有三个线程,但是一共卖了10张票,也就是说使用 Runnable 实现多线程可以达到资源共享目的。
查阅文档:
public class Thread implements Runnable
可以发现,Thread 类实现了 Runnable 接口。
参考资料:
http://xuyuanshuaaa.iteye.com/blog/1109498
http://fdcwqmst.blog.163.com/blog/static/164061455201010581442985/
本文介绍了Java中实现多线程的两种主要方法:继承Thread类和实现Runnable接口。通过具体示例对比这两种方法的特点及适用场景,特别是资源的共享问题。

被折叠的 条评论
为什么被折叠?



