對(duì)于 Java 多線程編程中的 implements Runnable 與 extends Thread,部分同學(xué)可能會(huì)比較疑惑缨称,它們之間究竟有啥區(qū)別和聯(lián)系呢?他們是不是沒(méi)啥區(qū)別隨便選呢藐握?實(shí)際中究竟該選擇哪一個(gè)呢额各?
甚至網(wǎng)上不少博客文章以訛傳訛得出不少謬論,那今天的走進(jìn)科學(xué)欄目將帶您一一揭開(kāi)謎底酒贬。
1又憨、區(qū)別:
其實(shí)這塊主要是圍繞著接口和抽象類(lèi)的區(qū)別以及一些設(shè)計(jì)原則而言的。
**1.1 ****Inheritance Option **:
The limitation with "extends Thread" approach is that if you extend Thread, you can not extend anything else . Java does not support multiple inheritance. In reality , you do not need Thread class behavior , because in order to use a thread you need to instantiate one anyway. On the other hand, Implementing the Runnable interface gives you the choice to extend any class you like , but still define behavior that will be run by separate thread.
**1.2 ****Reusability **:
In "implements Runnable" , we are creating a different Runnable class for a specific behavior job (if the work you want to be done is job). It gives us the freedom to reuse the specific behavior job whenever required. "extends Thread" contains both thread and job specific behavior code. Hence once thread completes execution , it can not be restart again.
**1.3 ****Object Oriented Design **:
Implementing Runnable should be preferred . It does not specializing or modifying the thread behavior . You are giving thread something to run. We conclude that Composition is the better way. Composition means two objects A and B satisfies has-a relationship. "extends Thread" is not a good Object Oriented practice.
**1.4 ****Loosely-coupled **:
"implements Runnable" makes the code loosely-coupled and easier to read . Because the code is split into two classes . Thread class for the thread specific code and your Runnable implementation class for your job that should be run by a thread code. "extends Thread" makes the code tightly coupled . Single class contains the thread code as well as the job that needs to be done by the thread.
**1.5 ****Functions overhead **:
"extends Thread" means inheriting all the functions of the Thread class which we may do not need . job can be done easily by Runnable without the Thread class functions overhead.
至此锭吨,個(gè)人是推薦優(yōu)先選擇 implements Runnable 蠢莺。
2、聯(lián)系:
2.1 其實(shí)Thread類(lèi)也是Runnable接口的子類(lèi)
public class Thread extends Object implements Runnable
2.2 啟動(dòng)線程都是 start() 方法
追蹤Thread中的start()方法的定義零如,可以發(fā)現(xiàn)此方法中使用了private native void start0();其中native關(guān)鍵字表示可以調(diào)用操作系統(tǒng)的底層函數(shù)躏将,這樣的技術(shù)稱(chēng)為JNI技術(shù)(java Native Interface)。
但是在使用Runnable定義的子類(lèi)中沒(méi)有start()方法考蕾,只有Thread類(lèi)中才有祸憋。此時(shí)觀察Thread類(lèi),有一個(gè)構(gòu)造方法:public Thread(Runnable targer)辕翰,此構(gòu)造方法接受Runnable的子類(lèi)實(shí)例夺衍,也就是說(shuō)可以通過(guò)Thread類(lèi)來(lái)啟動(dòng)Runnable實(shí)現(xiàn)的多線程。
2.3 網(wǎng)傳的一種繆論:用Runnable就可以實(shí)現(xiàn)資源共享喜命,而 Thread 不可以
有同學(xué)的例子是這樣的沟沙,參考: http://developer.51cto.com/art/201203/321042.htm :
package tmp;
class MyThread extends Thread {
private int ticket = 10;
private String name;
public MyThread(String name) {
this.name = name;
}
public void run() {
for (int i = 0; i < 500; i++) {
if (this.ticket > 0) {
System.out.println(this.name + "賣(mài)票---->" + (this.ticket--));
}
}
}
}
public class ThreadDemo {
public static void main(String[] args) {
MyThread mt1 = new MyThread("一號(hào)窗口");
MyThread mt2 = new MyThread("二號(hào)窗口");
MyThread mt3 = new MyThread("三號(hào)窗口");
mt1.start();
mt2.start();
mt3.start();
}
}
// 一號(hào)窗口賣(mài)票---->10
// 二號(hào)窗口賣(mài)票---->10
// 二號(hào)窗口賣(mài)票---->9
// 二號(hào)窗口賣(mài)票---->8
// 三號(hào)窗口賣(mài)票---->10
// 三號(hào)窗口賣(mài)票---->9
// 三號(hào)窗口賣(mài)票---->8
...
Runnable 代碼:
package tmp;
class MyThread1 implements Runnable {
private int ticket = 10;
private String name;
public void run() {
for (int i = 0; i < 500; i++) {
if (this.ticket > 0) {
System.out.println(Thread.currentThread().getName() + "賣(mài)票---->" + (this.ticket--));
}
}
}
}
public class RunnableDemo {
public static void main(String[] args) {
MyThread1 mt = new MyThread1();
Thread t1 = new Thread(mt, "一號(hào)窗口");
Thread t2 = new Thread(mt, "二號(hào)窗口");
Thread t3 = new Thread(mt, "三號(hào)窗口");
t1.start();
t2.start();
t3.start();
}
}
// 二號(hào)窗口賣(mài)票---->10
// 三號(hào)窗口賣(mài)票---->9
// 三號(hào)窗口賣(mài)票---->7
// 一號(hào)窗口賣(mài)票---->9
// 三號(hào)窗口賣(mài)票---->6
// 二號(hào)窗口賣(mài)票---->8
// 三號(hào)窗口賣(mài)票---->4
// 一號(hào)窗口賣(mài)票---->5
// 三號(hào)窗口賣(mài)票---->2
// 二號(hào)窗口賣(mài)票---->3
// 一號(hào)窗口賣(mài)票---->1
由此差別,有同學(xué)就得出了一個(gè)結(jié)論:用Runnable就可以實(shí)現(xiàn)資源共享壁榕,而 Thread 不可以矛紫,這是他們的主要差別之一。牌里。颊咬。
其實(shí)仔細(xì)看看代碼就知道务甥,這只是兩種寫(xiě)法的區(qū)別,根本就不是 implements Runnable 與 extends Thread 的區(qū)別:
MyThread1 mt = new MyThread1();
Thread t1 = new Thread(mt,"一號(hào)窗口");
Thread t2 = new Thread(mt,"二號(hào)窗口");
Thread t3 = new Thread(mt,"三號(hào)窗口");
////////////////
Thread t1 = new Thread(new MyThread1(),"一號(hào)窗口");
Thread t2 = new Thread(new MyThread1(),"二號(hào)窗口");
Thread t3 = new Thread(new MyThread1(),"三號(hào)窗口");
其實(shí)喳篇,想要“資源共享”敞临,Thread 也可以做到的:
private static int ticket = 10;
// 三號(hào)窗口賣(mài)票---->10
// 一號(hào)窗口賣(mài)票---->9
// 二號(hào)窗口賣(mài)票---->9
// 一號(hào)窗口賣(mài)票---->7
// 一號(hào)窗口賣(mài)票---->5
// 三號(hào)窗口賣(mài)票---->8
// 一號(hào)窗口賣(mài)票---->4
// 二號(hào)窗口賣(mài)票---->6
// 一號(hào)窗口賣(mài)票---->2
// 三號(hào)窗口賣(mài)票---->3
// 二號(hào)窗口賣(mài)票---->1
通過(guò) static 就可以實(shí)現(xiàn)擁有共同的ticket=10,但問(wèn)題也來(lái)了麸澜,你會(huì)發(fā)現(xiàn)一二號(hào)窗口都賣(mài)了第 9 張票挺尿。
3、資源共享帶來(lái)的問(wèn)題:多線程的線程安全問(wèn)題
上面的例子以及結(jié)果證明了多線程場(chǎng)景下炊邦,需要留意線程安全的問(wèn)題:
3.1 同步run()方法
public synchronized void run()
3.2 同步 class 對(duì)象
synchronized (Test.class)
3.3 同步某些靜態(tài)對(duì)象
private static final Object countLock = new Object();
synchronized (countLock) {
count++;
}
3.4 最后給個(gè)完整的例子编矾,模擬在線售票與查詢(xún):
package tmp;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicInteger;
public class Demo implements Runnable {
String name;
// static Integer tickets = 20;
private static AtomicInteger tickets = new AtomicInteger(20);
public Demo(String name) {
this.name = name;
}
public void run() {
for (int i = 1; i <= 20; i++) {
synchronized (tickets) {
if (tickets.get() > 0) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("我取票第" + ": " + tickets.getAndDecrement() + " 張票。");
// tickets--;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
}
System.out.println("==========現(xiàn)在查詢(xún)還剩" + ": " + tickets.get() + " 張票馁害。");
}
}
}
}
public static void main(String[] args) throws IOException {
Demo demo = new Demo("hello");
new Thread(demo).start();
new Thread(demo).start();
new Thread(demo).start();
}
}
到這兒窄俏,本期走進(jìn)科學(xué)也要跟大家說(shuō)聲再見(jiàn)了,其實(shí)聊著聊著感覺(jué)都快跑題了碘菜,多線程這塊話題很多凹蜈,很復(fù)雜,需要慢慢實(shí)踐與積累忍啸,祝大家玩的愉快踪区。
歡迎關(guān)注微信公眾號(hào):java大牛愛(ài)好者