Java中通过实现Runable接口,或者继承Thread类都可以实现线程。那么他们之间有什么区别和联系呢?
联系
- Thread类实现了Runable接口。
- 都需要重写
run
方法。
区别
- 实现Runnable接口的方法使程序更具有健壮性,可以避免单继承的局限;
- Runnable更容易实现资源共享,能多个线程同时处理一个资源,代码与数据是独立的。
例子
下面以买票为例,假设火车站共有三个售票窗口,每个窗口买一张票费时1秒钟,共有5张票。
- 继承Thread实现:
public class MyThread1 extends Thread {
private int tickets = 5;
@Override
public void run() {
String name = Thread.currentThread().getName();
int number = 0;
while (true) {
if (tickets <= 0) {
System.out.println("票已售罄");
break;
}
System.out.println(name + " 售出 " + ++number + " 张票,剩余票数:" + --tickets);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
new MyThread1().start();
new MyThread1().start();
new MyThread1().start();
}
}
运行结果:
Thread-1 售出 1 张票,剩余票数:4
Thread-0 售出 1 张票,剩余票数:4
Thread-2 售出 1 张票,剩余票数:4
Thread-1 售出 2 张票,剩余票数:3
Thread-0 售出 2 张票,剩余票数:3
Thread-2 售出 2 张票,剩余票数:3
Thread-1 售出 3 张票,剩余票数:2
Thread-0 售出 3 张票,剩余票数:2
Thread-2 售出 3 张票,剩余票数:2
Thread-1 售出 4 张票,剩余票数:1
Thread-0 售出 4 张票,剩余票数:1
Thread-2 售出 4 张票,剩余票数:1
Thread-1 售出 5 张票,剩余票数:0
Thread-0 售出 5 张票,剩余票数:0
Thread-2 售出 5 张票,剩余票数:0
票已售罄
票已售罄
票已售罄
可以看到,三个窗口均售出了5张票。
- Runnable实现
public class MyThread2 implements Runnable {
private int tickets = 5;
Object lock = new Object();
@Override
public void run() {
String name = Thread.currentThread().getName();
int number = 0;
while (true) {
if (tickets <= 0) {
System.out.println("票已售罄");
break;
}
System.out.println(name + " 售出 " + ++number + " 张票,剩余票数:" + --tickets);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyThread2 myThread = new MyThread2();
new Thread(myThread).start();
new Thread(myThread).start();
new Thread(myThread).start();
}
}
运行结果:
Thread-0 售出 1 张票,剩余票数:4
Thread-1 售出 1 张票,剩余票数:3
Thread-2 售出 1 张票,剩余票数:2
Thread-0 售出 2 张票,剩余票数:1
Thread-1 售出 2 张票,剩余票数:0
票已售罄
票已售罄
票已售罄
可以看到,三个窗口共同售出了5张票,且0号和1号窗口均售出2张,2号窗口售出1张。
注意:多线程共享资源时,如上例中3个Thread对象共同执行一个Runnable对象中的代码,可能会造成线程的不安全,比如leftTickets
可能会为-1。这就需要加入同步操作(即互斥锁,synchronized
或自定义),确保同一时刻只有一个线程在执行每次循环中的操作。