Java 并发编程之Runnable和Thread实现多线程的区别

时间:2021-02-16 17:32:10

Java中实现多线程通常有两种方式(其实从Java5开始有三种了,第三种先不说,ps:Java 并发编程之Runnable和Thread实现多线程的区别我暂时还没去研究):

1.继承Thread类
2.实现Runnable接口

虽说有两种实现方式,但是很明显在实际开发中实现Runnable这种方式明显要比继承Thread多多了,这是因为Runnable拥有天生的优势:

1.在多线程访问同一资源的情况下,用Runnable接口创建的线程可以处理同一资源,而用Thread创建的线程则独自处理,各自拥有自己的资源。
2.避免了Java单继承带来的局限性。

第二条优势实在太明显了,咱们这里不谈Java 并发编程之Runnable和Thread实现多线程的区别,咱们举一个烂大街的例子来说明下第一条优势。

首先通过继承Thread实现,上代码:

public class MyThread extends Thread {
private int mTicket = 5;

@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (mTicket > 0) {
System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
}
}

}
}


public class ThreadDemo {
public static void main(String arg0[]) {
ArrayList<Thread> threads = new ArrayList<>();

MyThread myThread1 = new MyThread();
MyThread myThread2 = new MyThread();
MyThread myThread3 = new MyThread();

threads.add(myThread1);
threads.add(myThread2);
threads.add(myThread3);

for (Thread thread : threads) {
thread.start();
}


try {
for (Thread thread : threads) {
thread.join();
}
} catch (InterruptedException e) {
e.printStackTrace();

}

System.out.println("所有线程执行完毕!");

}
}

运行结果:

Thread-0----mTicket = 5
Thread-2----mTicket = 5
Thread-2----mTicket = 4
Thread-2----mTicket = 3
Thread-2----mTicket = 2
Thread-2----mTicket = 1
Thread-1----mTicket = 5
Thread-1----mTicket = 4
Thread-0----mTicket = 4
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
所有线程执行完毕!

咱们开启了三个线程,每个线程各自卖了5张票,它们之间互不干扰很和谐。但是现实生活中其实并不是这样的,火车站官网售票是需要多个线程共同完成一个任务的,继承Thread这种方式实现不了,咱们试试实现Runnable接口这种方式,上代码:

public class MyRunnable implements Runnable {
private int mTicket = 5;

@Override
public void run() {
for (int i = 0; i < 10; i++) {
if (mTicket > 0) {
System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
}
}
}
}


public class ThreadDemo {
public static void main(String arg0[]) {
ArrayList<Thread> threads = new ArrayList<>();

MyRunnable runnable = new MyRunnable();

Thread thread1 = new Thread(runnable);
Thread thread2 = new Thread(runnable);
Thread thread3 = new Thread(runnable);


threads.add(thread1);
threads.add(thread2);
threads.add(thread3);

for (Thread thread : threads) {
thread.start();
}


try {
for (Thread thread : threads) {
thread.join();
}
} catch (InterruptedException e) {
e.printStackTrace();

}

System.out.println("所有线程执行完毕!");

}
}

第一次运行结果:

Thread-1----mTicket = 4
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
Thread-0----mTicket = 5
所有线程执行完毕!

第二次运行结果:

Thread-0----mTicket = 5
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
Thread-2----mTicket = 4
Thread-1----mTicket = 5
所有线程执行完毕!

为何出现两种不同的结果呢,因为3个Thread对象共同执行Runnable对象中的代码,线程执行的时机又是难以预测的,很有可能会造成线程不安全。这时只要加上同步锁,确保同一时刻只有一个线程在执行for循环里的代码,就可以保证线程安全了。咱们修改下MyRunnable的代码,上代码:

public class MyRunnable implements Runnable {
private int mTicket = 5;

@Override
public void run() {
for (int i = 0; i < 10; i++) {
synchronized (this) {
if (mTicket > 0) {
System.out.println(Thread.currentThread().getName() + "----mTicket = " + mTicket--);
}
}

}
}
}

第一次运行结果:

Thread-0----mTicket = 5
Thread-0----mTicket = 4
Thread-0----mTicket = 3
Thread-0----mTicket = 2
Thread-0----mTicket = 1
所有线程执行完毕!

第二次运行结果:

Thread-0----mTicket = 5
Thread-0----mTicket = 4
Thread-1----mTicket = 3
Thread-1----mTicket = 2
Thread-1----mTicket = 1
所有线程执行完毕!

嘿嘿,不管运行多少次,发现结果都是一样的。