通过继承Thread类实现一个线程对象存在一个弊端,那就是java的单继承问题
以及继承方式实现多线程需要实例多个对象,这些对象之间如果要做到数据共享只能通过静态的方式
Runnable接口有效的解决了这些问题
抽象方法
Runnable接口只有一个run方法,实现类只需要重写这个方法即可。
而它的实现类如果想要实现多线程,那么只需要创建含有Runnable类型参数的构造方法的线程对象即可
简单的案例 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| Runnable runnable = new Runnable() { private int num = 0; @Override public void run() { while (num < 50) System.out.println(num++); } }; Thread thread1 = new Thread(runnable); Thread thread2 = new Thread(runnable,"run2"); System.out.println(thread1.getName()); System.out.println(thread2.getName()); thread1.start(); thread2.start();
|
对比继承Thread类的优势
接下来以一个经典的售票案例对两者作对比
假设电影院目前有《喜羊羊与灰太狼》票100张《仙剑奇侠传》票100张
要求先出售喜羊羊与灰太狼再出售仙剑奇侠传并且有三个出售窗口,购票有100ms的延迟
首先是继承Thread类的实现方式,代码如下 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public static void main(String[] args) { Cinema cinema1 = new Cinema("窗口1"); Cinema cinema2 = new Cinema("窗口2"); Cinema cinema3 = new Cinema("窗口3"); cinema1.start(); cinema2.start(); cinema3.start(); cinema1.addTicket(new Ticket("喜羊羊与灰太狼",100)); cinema1.addTicket(new Ticket("仙剑奇侠传",100)); } class Cinema extends Thread{ public Cinema(){} public Cinema(String name){super(name);} private static LinkedList<Ticket> tickets = new LinkedList<>(); public void addTicket(Ticket ticket){tickets.addLast(ticket);} @Override public void run() { while (true){ if (tickets.size() > 0) { Ticket first = tickets.getFirst(); while (first.getNumber() > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " : " + first); first.setNumber(first.getNumber() - 1); } if (tickets.size() > 0 && tickets.getFirst().getNumber() <= 0) tickets.removeFirst(); } } } } class Ticket{ private String name; private int number; public Ticket() {} public Ticket(String name, int number) { this.name = name; this.number = number; } @Override public String toString() { return "Ticket{" + name + ", 第" + number + "张}"; } }
|
运行结果如下 :

然后是实现Runnable接口的实现方式 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| public static void main(String[] args) { LinkedList<Ticket> linked = new LinkedList<>(); linked.addLast(new Ticket("喜羊羊与灰太狼", 100)); linked.addLast(new Ticket("仙剑奇侠传", 100)); while (true) { if (linked.size() > 0) { Thread thread1 = new Thread(linked.getFirst(), "窗口1"); Thread thread2 = new Thread(linked.getFirst(), "窗口2"); Thread thread3 = new Thread(linked.getFirst(), "窗口3"); thread1.start(); thread2.start(); thread3.start(); try { thread1.join(); thread2.join(); thread3.join(); } catch (InterruptedException e) { e.printStackTrace(); } linked.removeFirst(); } } } class Ticket{ private String name; private int number; public Ticket() {} public Ticket(String name, int number) { this.name = name; this.number = number; } @Override public String toString() { return "Ticket{" + name + ", 第" + number + "张}"; } @Override public void run() { while (number > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " : " + this); number--; } } }
|
运行结果如下 :

大致来说,实现Runnable接口的方法实现多线程解决了单继承的麻烦(内部类实现多继承的除外)
需要共享的成员不必定义为静态属性,一个对象可以被多个线程执行等...
上述代码存在的问题
其实观察代码的执行结果,不难发现。都有出现同票甚至出现了负票
出现这种情况的原因是由于CPU的执行具有原子性,即每次只执行最基础的操作
比如i++这个操作就要分为三步,其中每一步都可能由于当前线程被其他线程抢占而执行了其他操作
要解决这个问题,就要用到同步的相关技巧
Synchronized线程同步
相关习题
线程练习