首先,多线程的实现方式两种:一种是继承Thread类,另一种是实现Runnable接口。
那么这两种方法的区别何在?该如何选择?
第一:他们之间的关系
查看J2EE的API看到
Thread类中: public class Thread extends Object implements Runnable
Runnable接口:public interfaceRunnable
明显可知两者:Thread类是Runnable接口的一个实现类,那么Runnable接口是何用?
文档解释:
Runnable 接口应该由那些打算通过某一线程执行其实例的类来实现。类必须定义一个称为 run 的无参数方法。
设计该接口的目的是为希望在活动时执行代码的对象提供一个公共协议。例如,Thread 类实现了 Runnable。激活的意思是说某个线程已启动并且尚未停止。
也就是说Runnable提供的是一种线程运行规范,具体运行线程需要通过它的实现类
第二:通过源码分析
我们以public class Thread1 extends Thread 这个自定义线程来追本溯源:首先查看Thread类
其中定义了一个private Runnable target; 定义了Runnable类型的属性target,查看哪里有引用
几个方法:
private void init(ThreadGroup g, Runnable target, String name, long stackSize) {。。。} public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } public Thread(ThreadGroup group, Runnable target) { init(group, target, "Thread-" + nextThreadNum(), 0); } public Thread(String name) { init(null, null, name, 0); } public Thread(ThreadGroup group, String name) { init(group, null, name, 0); } public Thread(Runnable target, String name) { init(null, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name) { init(group, target, name, 0); } public Thread(ThreadGroup group, Runnable target, String name, long stackSize) { init(group, target, name, stackSize); }以上列出了Thread的一个初始化方法init()和所有的构造方法:可以知道,构造方法需要调用init()方法初始化线程对象,有一个Runnable类型的target对象也参与初始化。
我们所知道的Thread类进行运行线程时是调用start()方法,我们也来查看这个方法:
可知:当调用这个start()方法时,使该线程开始执行;Java 虚拟机调用该线程的 run 方法。结果是两个线程并发地运行;当前线程(从调用返回给 start 方法)和另一个线程(执行其 run 方法)。
这个方法仅作了线程状态的判断(保证一个线程不多次启动,多次启动在JVM看来这是非法的),然后把该线程添加到线程组(不多做解释)等待运行。
那么继续看run()方法:thread的run只是调用了runable的对像,再调用runable对象的方法
可知,当Runnable实现类对象没有内容为null,则方法什么都不执行,如果有实现类对象,就调用它实现类对象实现的run()方法。这让我们想到了一个经典的设计模式:代理模式
到此我们知道:线程的运行是依靠Thread类中的start()方法执行,并且由虚拟机调用run()方法,所以我们必须实现run()方法
那么还是要看一下Runnable接口的设计:
public interface Runnable { /** * When an object implementing interface <code>Runnable</code> is used * to create a thread, starting the thread causes the object's * <code>run</code> method to be called in that separately executing * thread. * <p> * The general contract of the method <code>run</code> is that it may * take any action whatsoever. * * @see java.lang.Thread#run() */ public abstract void run(); }可知它只有一个抽象的run()方法,完全是实实在在的线程运行规范
第三:通过他们之间的设计模式:代理模式 再次深入
代理模式如图:
可知,Thread也是Runnable接口的子类,但其没有完全实现run()方法,所以说如果继承Thread类实现多线程,仍旧需要覆写run()方法。
看两种实现多线程的基本方式
继承Thread:
class MyThread extends Thread{ private int ticket = 5; public void run(){ for(int i = 0;i<100;i++){ if(ticket>0){//判断是否还有剩余票 System.out.println("卖票,ticket = "+ticket--); } } } }; public class ThreadDemo04{ public static void main(String args[]){ MyThread mt1 = new MyThread(); MyThread mt2 = new MyThread(); MyThread mt3 = new MyThread(); mt1.start();//调用线程主体让其运行 mt2.start();//三个地方同时卖票 mt3.start(); } }; 实现Runnable: class MyThread implements Runnable{ private int ticket=5; public void run(){ for(int i = 0;i<100;i++){ if(ticket>0){ System.out.println("卖票,ticket = "+ticket--); } } } }; public class RunnableDemo02{ public static void main(String args[]){ MyThread my1 = new MyThread(); new Thread(my1).start(); //启动三个线程 new Thread(my1).start(); //共享my1中资源 new Thread(my1).start(); } };