您的位置:首页 > 编程语言 > Java开发

Java 多线程:Thread 和 Runnable 区别

2017-01-11 19:03 537 查看
本文内容大多基于官方文档和网上前辈经验总结,经过个人实践加以整理积累,仅供参考。

继承 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 多线程