目录:
多线程的实现方法:
继承Thread类
实现Runnable接口
-------------------------------------------------------------------------------------
继承Thread类之后,需要覆盖父类的 public void run() 方法,作为线程的主方法。
所有线程的执行一定是并发的,即:同一个时间段上会有多个线程交替执行。为了达到这样的目的,绝对不能直接调用run()方法,而是应该调用Thread类的start()方法启动多线程。
调用 start() 方法和调用 run() 方法的对比:
public class MyThread extends Thread { private String name; public MyThread(String name) { this.name = name; } @Override public void run() { for(int i=0; i<10; i++) { System.out.println(name + "打印:" + i); } } public static void main(String[] args) { MyThread mt1 = new MyThread("线程A"); MyThread mt2 = new MyThread("线程B"); MyThread mt3 = new MyThread("线程C"); mt1.start(); mt2.start(); mt3.start(); } }
运行结果:(三个线程同时且交替执行,没有固定的执行顺序)
public class MyThread extends Thread { private String name; public MyThread(String name) { this.name = name; } @Override public void run() { for(int i=0; i<5; i++) { System.out.println(name + "打印:" + i); } } public static void main(String[] args) { MyThread mt1 = new MyThread("线程A"); MyThread mt2 = new MyThread("线程B"); MyThread mt3 = new MyThread("线程C"); mt1.run(); mt2.run(); mt3.run(); } }
运行结果:(三个程序依次顺序执行)
打开Thread类源代码中start()方法的部分:
public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } } private native void start0();
native关键字是指调用操作系统的方法,start0()方法是所在操作系统的方法。
由于线程的启动需要牵扯到操作系统中资源的分配问题,所以具体的线程的启动应该根据不同的操作系统有不同的实现。而JVM根据不同的操作系统中定义的start0()方法进行不同的实现。这样,在多线程的层次上start0()方法的名称不改变,而不同的操作系统有不同的实现。
原理图
结论:只有Thread类的start()方法才能进行操作系统资源的分配,所以启动多线程的方式永远就是Thread类的start()方法。
一般使用这种方式来实现多线程,因为这样可以避免继承Thread类的单继承局限。
package test; public class MyThread implements Runnable { private String name; public MyThread(String name) { this.name = name; } @Override public void run() { for(int i=0; i<5; i++) { System.out.println(name + "打印:" + i); } } public static void main(String[] args) { MyThread mt1 = new MyThread("线程A"); MyThread mt2 = new MyThread("线程B"); MyThread mt3 = new MyThread("线程C"); new Thread(mt1).start(); new Thread(mt2).start(); new Thread(mt3).start(); } }
4. Thread类 与 Runnable接口 的联系与区别
联系:
Thread类是实现了Runnable接口的类。
区别:
Runnable接口实现的多线程要比Thread类实现的多线程更方便的表示出数据共享的概念。
范例:希望有三个线程进行卖票
//使用Thread类实现 public class MyThread extends Thread { private String name; int tickets = 5; public MyThread(String name) { this.name = name; } @Override public void run() { while(tickets>0) { System.out.println(name + "买票出一张票,剩余票数:" + (--tickets)); } } public static void main(String[] args) { MyThread mt1 = new MyThread("线程A"); MyThread mt2 = new MyThread("线程B"); MyThread mt3 = new MyThread("线程C"); mt1.start(); mt2.start(); mt3.start(); } } /* 线程C买票出一张票,剩余票数:4 线程A买票出一张票,剩余票数:4 线程B买票出一张票,剩余票数:4 线程A买票出一张票,剩余票数:3 线程C买票出一张票,剩余票数:3 线程A买票出一张票,剩余票数:2 线程B买票出一张票,剩余票数:3 线程A买票出一张票,剩余票数:1 线程C买票出一张票,剩余票数:2 线程C买票出一张票,剩余票数:1 线程A买票出一张票,剩余票数:0 线程B买票出一张票,剩余票数:2 线程B买票出一张票,剩余票数:1 线程B买票出一张票,剩余票数:0 线程C买票出一张票,剩余票数:0 */
//使用Runnable接口实现 public class MyThread implements Runnable { int tickets = 5; @Override public void run() { while(tickets>0) { System.out.println(Thread.currentThread().getName() + "买票出一张票,剩余票数:" + (--tickets)); } } public static void main(String[] args) { MyThread mt = new MyThread(); new Thread(mt,"线程A").start(); new Thread(mt,"线程B").start(); new Thread(mt,"线程C").start(); } } /* 线程B买票出一张票,剩余票数:3 线程A买票出一张票,剩余票数:4 线程C买票出一张票,剩余票数:2 线程A买票出一张票,剩余票数:0 线程B买票出一张票,剩余票数:1 */
//同一个线程不能重复启动,否则会出现异常 public class MyThread extends Thread { int tickets = 5; @Override public void run() { while(tickets>0) { System.out.println("买票出一张票,剩余票数:" + (--tickets)); } } public static void main(String[] args) { MyThread mt = new MyThread(); mt.start(); mt.start(); mt.start(); } } /* Exception in thread "main" 买票出一张票,剩余票数:4 买票出一张票,剩余票数:3 买票出一张票,剩余票数:2 买票出一张票,剩余票数:1 买票出一张票,剩余票数:0 java.lang.IllegalThreadStateException at java.lang.Thread.start(Unknown Source) at test.MyThread.main(MyThread.java:17) */
图释:
使用Thread类的内存情况
使用Runnable接口的内存情况
面试题:请解释多线程的两种实现方式以及区别,并用代码验证?
答:多线程需要一个线程的主类,这个类要么继承Thread类,要么实现Runnable接口;
使用Runnable接口要比Thread类更好地实现数据共享的操作,并且使用Runnable接口可以避免单继承局限。
代码如上。