在 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/