java实现多线程的常用方式有两种:继承Thread类、实现Runnable接口实现run方法。
- 继承Thread实现方式:
public class ThreadTest extends Thread{
private int ticket = 100;
public void run() {
while(ticket > 0){
ticket--;
System.out.println(Thread.currentThread().getName()+"tickets:"+ticket);
}
}
public static void main(String[] args){
ThreadTest threadTest = new ThreadTest();
threadTest.start();
System.out.println("主线程执行结束!");
}
}
- 实现Runnable实现方式:
public class RunableTest implements Runnable{
private int ticket = 100;
@Override
public void run() {
while(ticket > 0){
ticket--;
System.out.println(Thread.currentThread().getName()+"tickets:"+ticket);
}
}
public static void main(String[] args){
RunableTest runableTest = new RunableTest();
Thread testThread = new Thread(runableTest);
testThread.start();
System.out.println("主线程结束!");
}
}
可以看出这两种方式实现多线程的方式非常相似,那它们之间有什么区别呢?
设想个场景,火车站有100张票,有4个窗口进行售票,如何实现4个窗口共享100张票呢?
- 案例一:
public class ThreadTest extends Thread{
private int tickets = 100;
public void run() {
while(tickets > 0){
tickets--;
System.out.println(Thread.currentThread().getName()+"tickets:"+tickets);
}
}
public static void main(String[] args){
ThreadTest threadTest1 = new ThreadTest();
ThreadTest threadTest2 = new ThreadTest();
ThreadTest threadTest3 = new ThreadTest();
ThreadTest threadTest4 = new ThreadTest();
threadTest1.start();
threadTest2.start();
threadTest3.start();
threadTest4.start();
System.out.println("主线程执行结束!");
}
}
不难看出“案例一”没有共享tickets资源,而是每个线程独自创建了一个tickets资源,就相当于每个窗口都有100张票进行销售。
- 案例二:
public class ThreadTest extends Thread{
private static int tickets = 100;
public void run() {
while(tickets > 0){
tickets--;
System.out.println(Thread.currentThread().getName()+"tickets:"+tickets);
}
}
public static void main(String[] args){
ThreadTest threadTest1 = new ThreadTest();
ThreadTest threadTest2 = new ThreadTest();
ThreadTest threadTest3 = new ThreadTest();
ThreadTest threadTest4 = new ThreadTest();
threadTest1.start();
threadTest2.start();
threadTest3.start();
threadTest4.start();
System.out.println("主线程执行结束!");
}
}
“案例二”把成员变量用static修饰后,tickets属于ThreadTest类,不再属于ThreadTest创建的对象,这样就实现了多个线程对统一资源的共享。
- 案例三:
public class ThreadTest extends Thread{
private int tickets = 100;
public void run() {
while(tickets > 0){
tickets--;
System.out.println(Thread.currentThread().getName()+"tickets:"+tickets);
}
}
public static void main(String[] args){
ThreadTest threadTest1 = new ThreadTest();
threadTest1.start();
threadTest1.start();
threadTest1.start();
threadTest1.start();
System.out.println("主线程执行结束!");
}
}
“案例三”创建了一个ThreadTest对象启动了4次,系统它会启动4个线程来处理tickets,通过结果分析并没有创建4个线程处理,仅仅创建了一个线程。可见一个线程对象只能启动一个线程,无论调用几次start方法,都只是一个线程在执行。
- 案例四:
public class RunableTest implements Runnable{
private int tickets = 100;
@Override
public void run() {
while(tickets > 0){
tickets--;
System.out.println(Thread.currentThread().getName()+"tickets:"+tickets);
}
}
public static void main(String args[]){
RunableTest runableTest = new RunableTest();
Thread thread1 = new Thread(runableTest);
Thread thread2 = new Thread(runableTest);
Thread thread3 = new Thread(runableTest);
Thread thread4 = new Thread(runableTest);
thread1.start();
thread2.start();
thread3.start();
thread4.start();
System.out.println("主线程执行结束!");
}
}
“案例四”创建了四个线程,每个线程都调用runableTest对象的run方法,也就是说每个线程同时共享了runableTest对象中的tickets成员变量。
那么继承Thread和实现Runnable接口实现多线程有什么区别呢?
由以上几个案例可见,即使实现Runnable接口实现多线程,也是要通过创建Thread来启动实现的,所以使用继承Thread方式实现多线程在代码上可以简化。
Java不像C++支持多继承,所以说在一个类已经有个父类的时候就无法继承Thread来实现多线程,所以实现Runnable接口在扩展性上要比继承Thread方式实现好。
- 分析案例不难看出,在多个线程同时执行一块代码区域的时候使用实现Runnable接口的方式更加合适。把虚拟CPU(线程)同程序的代码,数据有效的分离,较好地体现了面向对象的设计思想。
备注:以上案例并不能实现多线程线程安全的共享tickets资源,案例的目标是为了描述两种实现多线程的方式是如何实现共享资源的(非线程安全)。如果想了解如何保证资源线程安全的被多个线程处理,请持续关注笔者的《Java多线程》系列相关博客,谢谢!