再次理解java多執行緒的實現 Thread 和Runnable的區別
Thread和Runnable的區別
如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable介面的話,則很容易的實現資源共享。
main函式,例項化執行緒物件也有所不同,
extends Thread :t.start();
implements Runnable : new Thread(t).start();
總結:
實現implements Runnable介面比繼承 extends Thread類所具有的優勢:
1):適合多個相同的程式程式碼的執行緒去處理同一個資源
2):可以避免java中的單繼承的限制(不能訪問父類的私有成員?)
3):增加程式的健壯性,程式碼可以被多個執行緒共享,程式碼和資料獨立
4):執行緒池只能放入實現Runable或callable類執行緒,不能直接放入繼承Thread的類
為什麼這麼說類繼承Thread,則不適合資源共享。但是如果實現了Runable介面的話,則很容易的實現資源共享:舉個賣票的例子:
以賣票程式為例,通過Thread類完成:
- package org.demo.dff;
- class MyThread extends Thread{
- privateint ticket=10;
- publicvoid run(){
- for(int i=0;i<20;i++){
- if(this.ticket>0){
- System.out.println("賣票:ticket"
- }
- }
- }
- };
下面通過三個執行緒物件,同時賣票:
- package org.demo.dff;
- publicclass ThreadTicket {
- publicstaticvoid main(String[] args) {
- MyThread mt1=new MyThread();
- MyThread mt2=new MyThread();
- MyThread mt3=new MyThread();
- mt1.start();//每個執行緒都各賣了10張,共賣了30張票
- mt2.start();//但實際只有10張票,每個執行緒都賣自己的票
- mt3.start();
- }
- }
如果用Runnable就可以實現資源共享,下面看例子:
- package org.demo.runnable;
- class MyThread implements Runnable{
- privateint ticket=10;
- publicvoid run(){
- for(int i=0;i<20;i++){
- if(this.ticket>0){
- System.out.println("賣票:ticket"+this.ticket--);
- }
- }
- }
- }
- package org.demo.runnable;
- publicclass RunnableTicket {
- publicstaticvoid main(String[] args) {
- MyThread mt=new MyThread();
- new Thread(mt).start();//同一個mt,但是在Thread中就不可以,如果用同一
- new Thread(mt).start();//個例項化物件mt,就會出現異常
- new Thread(mt).start();
- }
- };
雖然現在程式中有三個執行緒,但是一共賣了10張票,也就是說使用Runnable實現多執行緒可以達到資源共享目的。
為什麼Java 允許實現多介面,卻不允許多繼承(C++允許)?
如果多繼承,會出現語義不明。類c同時繼承A,B,AB都有fun()方法,呼叫c.fun() 會不清楚究竟是呼叫誰的。
多介面呢?如果類c同時實現介面A,B ,而介面中的都是抽象方法();呼叫方法時不會出現不明確,畢竟介面中的都是抽象方法,而且 超類 ??的任何方法都需要在子類中覆蓋而實現。所以呼叫介面的方法時,其實是呼叫自身。
package com.whr.demo; public interface A { void fun(); }
package com.whr.demo;
public interface B {
void fun();
}
package com.whr.demo;
public class Demo implements A,B {
@Override
public void fun() {
System.out.println("我自己來實現介面中的抽象方法"); //繼承介面A,B。但是方法的具體實現是自己本函式實現的。接口裡面都是抽象方法。
}
public static void main(String[] args) {
Demo demo = new Demo();
demo.fun();
}
}
這裡就要再說一下 介面 和 抽象類的關係了:(之前一直沒有總結)
看到上面,直到為什麼介面可以被多實現,因為->裡面的方法全部是抽象的!什麼是抽象?就是final static 不能被呼叫,只能被override?
介面:比抽象類更抽象。是物件 動作的抽象。描述物件能做什麼。介面中方法常用一些,成員變數用得少一些。
1.裡面所有的方法都是抽象abstract的,沒有任何實現,所有可變的東西都應該歸屬到實現類中Runnable()介面的run()方法,很明顯體現。(start方法是Thread的);;;介面中的方法必須是public 的,要可以被重寫,不然繼承接口乾什麼。
2.介面中的成員變數只有一種型別,public static final ,所以可以直接省去修飾符。
-----------public 。保證所有實現類的共有。
-----------static :所有實現類都只有這一份,避免重名。如果實現類的第二個介面也同名,那麼儲存時候就會報錯(是好事)。
-----------既然抽象類,肯定final,大家都用的,不能隨便改。否則違反設計模式的OCP開閉原則->穩定靈活的系統。
3.不含構造器。
[修飾符] interface 介面名 [extends 父介面名列表]{
[public] [static] [final] 常量;
[public] [abstract] 方法;//沒有方法體
}
public interface CalInterface {
final float PI=3.14159f;//定義用於表示圓周率的常量PI
float getArea(float r);//定義一個用於計算面積的方法getArea()
float getCircumference(float r);//定義一個用於計算周長的方法getCircumference()
}
一個類實現多個介面,同名變數的訪問
interface X {
public static final String name="123";
}
interface Y {
public static final String name="456";
}
public class Z implements X,Y {
public static void main (String [] args){
System.out.println(X.name);//如果不定義為 static 的成員變數,如果實現的兩個介面中有同名變數,則不能引用。
System.out.println(Y.name); //不能寫做 Z t1 = new Z(); System.out.print(z.name);這時候會報錯,定義為static 就是為了在編譯時就能發現錯誤。
}
}
/*
* 執行結果:
* 123
* 456
* */
抽象類:是物件 根源的抽象。本質區別於別的類。描述物件是什麼。
1.裡面的方法,抽象或不抽象方法。抽象方法都放給子類來具體實現。
2.成員變數 可以是static ,也可以普通的成變。
3.可以包含構造器,並不建立物件,而是讓子類可以呼叫構造器完成屬於抽象類的初始化。
抽象類中的功能>>介面。但是訂一起來代價高。而且java單繼承侷限了,得在這個類裡面寫出所有子類的共性。
//抽象類的宣告
abstract class Animal {
String type;
String name;
int age;
int weight;
void eat() {
System.out.println("動物愛吃飯");
}
//抽象方法在抽象類中只能宣告,不能具體實現
abstract void breath();
void sleep() {
System.out.println("動物在睡覺");
}
}
//由子類去繼承父類抽象類
class tiger extends Animal{
@Override
//在此處實現抽象方法 breath()
void breath() {
System.out.println("老虎在呼吸");
}
}
public class first_for {
public static void main(String [] args){
// 錯誤,程式會報錯
//報錯原因:抽象類不能進行例項化操作
//Animal Tiger = new Animal();
//只能用子類進行例項化 *************66666666666
Animal Tiger = new tiger();
Tiger.breath();
}
}