Java 多线程:Thread 和 Runnable 区别
2017-01-11 19:03
537 查看
本文内容大多基于官方文档和网上前辈经验总结,经过个人实践加以整理积累,仅供参考。
继承 java.lang.Thread 类和实现 java.lang.Runnable 接口是 Java 实现线程的两种方法,通常以实现 Runnable 接口为主,因为相比之下 Runnable 有如下优势:
(1) Thread 实现
测试代码新建了3个 CustomThread 类对象,实际上这三个对象的功能都是打印当前线程名称,测试结果:
![](https://img-blog.csdn.net/20170111190950720?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2lsZW50X1BhbGFkaW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(2) Runnable 实现
测试代码新建了3个 java.lang.Thread 类对象,共享同一个 CustomRunnable 对象,CustomRunnable 对象功能就是打印当前线程名称,测试结果:
![](https://img-blog.csdn.net/20170111190323349?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2lsZW50X1BhbGFkaW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
(1) Thread 实现
测试结果:
![](https://img-blog.csdn.net/20170111192657078?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2lsZW50X1BhbGFkaW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
通过测试结果可以看出,Thread 实现相当于多个线程分别完成各自的任务。以售票为例,根据售票窗口数量(线程数量)均分总票数,各窗口负责销售分配给自己的票数,这样可能会出现某些窗口提前销售完所有自己负责的票数,而有些窗口却还有票未售完。
(2) Runnable 实现
测试结果:
![](https://img-blog.csdn.net/20170111193018187?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvU2lsZW50X1BhbGFkaW4=/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast)
通过测试结果可以看出,所有线程共同完成一个任务。以售票为例,所有窗口都会一直售票到最后一张票售出。
需要额外注意,从打印结果可以看出,ticket 的售出顺序并非倒序,这是因为线程执行时机难以预测,而 –tickets 并非原子操作!
多个 Thread 对象共同执行同一个 Runnable 对象中的代码是非线程安全的,如果代码中没有加线程休眠的代码,很可能后续会打印出 Left tickets number : -1,因为一个线程在判断剩余票数还剩 1 张时,还没有来得及减 1,便切换到另一个线程将票数减 1,变为了 0,那么接下来之前的线程再将票数减 1,便得到了 -1。
继承 java.lang.Thread 类和实现 java.lang.Runnable 接口是 Java 实现线程的两种方法,通常以实现 Runnable 接口为主,因为相比之下 Runnable 有如下优势:
1 代码和数据分离,可被多个线程共享
例:实现打印当前线程名称的功能(1) Thread 实现
class CustomThread extends Thread { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { Thread t1 = new CustomThread(); Thread t2 = new CustomThread(); Thread t3 = new CustomThread(); t1.start(); t2.start(); t3.start(); }
测试代码新建了3个 CustomThread 类对象,实际上这三个对象的功能都是打印当前线程名称,测试结果:
(2) Runnable 实现
class CustomRunnable implements Runnable { @Override public void run() { System.out.println(Thread.currentThread().getName()); } } public static void main(String[] args) { Runnable runnable = new CustomRunnable(); Thread t1 = new Thread(runnable); Thread t2 = new Thread(runnable); Thread t3 = new Thread(runnable); t1.start(); t2.start(); t3.start(); }
测试代码新建了3个 java.lang.Thread 类对象,共享同一个 CustomRunnable 对象,CustomRunnable 对象功能就是打印当前线程名称,测试结果:
2 适用于多个相同逻辑的线程处理同一资源
通过经典的售票系统案例展示继承 java.lang.Thread 类和实现 java.lang.Runnable 接口的差异(1) Thread 实现
class TicketThread extends Thread { private int tickets = 5; @Override public void run() { for (int i = 0; i < 10; i++) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + " Left tickets number : " + (--tickets)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName() + " All tickets have been saled!"); break; } } } } public static void main(String[] args) { new TicketThread().start(); new TicketThread().start(); new TicketThread().start(); }
测试结果:
通过测试结果可以看出,Thread 实现相当于多个线程分别完成各自的任务。以售票为例,根据售票窗口数量(线程数量)均分总票数,各窗口负责销售分配给自己的票数,这样可能会出现某些窗口提前销售完所有自己负责的票数,而有些窗口却还有票未售完。
(2) Runnable 实现
class TicketRunnable implements Runnable { private int tickets = 5; @Override public void run() { for (int i = 0; i < 10; i++) { if (tickets > 0) { System.out.println(Thread.currentThread().getName() + " Left tickets number : " + (--tickets)); try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } else { System.out.println(Thread.currentThread().getName() + " All tickets have been saled!"); break; } } } } public static void main(String[] args) { Runnable ticket = new TicketRunnable(); new Thread(ticket).start(); new Thread(ticket).start(); new Thread(ticket).start(); }
测试结果:
通过测试结果可以看出,所有线程共同完成一个任务。以售票为例,所有窗口都会一直售票到最后一张票售出。
需要额外注意,从打印结果可以看出,ticket 的售出顺序并非倒序,这是因为线程执行时机难以预测,而 –tickets 并非原子操作!
多个 Thread 对象共同执行同一个 Runnable 对象中的代码是非线程安全的,如果代码中没有加线程休眠的代码,很可能后续会打印出 Left tickets number : -1,因为一个线程在判断剩余票数还剩 1 张时,还没有来得及减 1,便切换到另一个线程将票数减 1,变为了 0,那么接下来之前的线程再将票数减 1,便得到了 -1。
3 避免 Java 单继承特性的局限,面向接口的编程更为合理
相关文章推荐
- 【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
- Java多线程实现接口Runnable和继承Thread区别(建议使用Runnable)
- Java中 Thread和Runnable实现多线程的区别和联系
- java多线程之Thread与runnable的区别
- java多线程实现(thread和runnable接口两种方式以及其区别)
- Java学习从菜鸟变大鸟之三 多线程中Thread 和Runnable的区别与运用
- 多线程——Java中继承Thread类与实现Runnable接口的区别
- Java基础知识强化之多线程笔记05:Java中继承thread类 与 实现Runnable接口的区别
- Java 多线程实现接口Runnable和继承Thread区别 (2
- Java多线程(四) Thread和Runnable之区别(售票系统)
- JAVA基础(多线程Thread和Runnable的使用区别
- 【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
- Java中两种多线程实现方法:Thread类和Runnable接口的联系与区别
- 【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)
- Java学习从菜鸟变大鸟之三 多线程中Thread 和Runnable的区别与运用
- Java多线程Thread和Runnable的区别
- java多线程的runnable与thread的区别
- Java 多线程实现接口Runnable和继承Thread区别
- 【Java并发编程】Runnable和Thread实现多线程的区别
- 【Java并发编程】之六:Runnable和Thread实现多线程的区别(含代码)