天天看點

黑馬程式員——黑馬學習日志之九 多線程

------- android教育訓練、java教育訓練、期待與您交流! ----------

黑馬學習日志之九 多線程

1 線程

程序:一個正在執行的程式,每個程序執行都有一個執行順序,該順序是一個執行路徑,或者叫一個控制單元。

線程:程序中的一個獨立的控制單元,就是程序中的一個子程式,一個獨立的執行路徑。線程控制程序的執行。

主線程:JVM啟動時會有一個程序Java.exe,該程序中至少有一個線程負責Java.exe 執行,這個線程運作代碼存在于main方法中,該線程稱為主線程。

更細節說明JVM,JVM不止一個線程,還有負責垃圾回收機制的線程。

注意:因為多個線程都擷取cpu的執行權。cpu執行到誰,誰就運作。明确一點,在某一個時刻,隻能有一個程式在運作。(多核除外)cpu在做着快速的       切換,以達到看上去是同時運作的效果。我們可以形象把多線程的運作行為在互相搶奪cpu的執行權。這就是多線程的一個特性:随機性。誰搶       到誰執行,至于執行多長,cpu說的算。

2 建立線程

   建立線程,繼承Thread類

建立線程方法一:将類聲明為 Thread 的子類。該子類應重寫 Thread 類的 run 方法。

(1)自定義類繼承Thread類

(2)重寫run方法

(3)将要執行的代碼寫在run方法中

(4)建立Thread類的子類對象

(5)調用start方法開啟線程。

為什麼要覆寫run()方法:Thread類用于描述線程,該類就定義了一個功能,用于存  儲線程要運作的代碼,該存儲功能就是run方法。也就是  說Thread類中的run方法用于存儲線程要運作的方法。

例子:

// 自定義建立線程

class MyThread extends Thread {

public void run() {

for (int i = 0; i < 1000; i++) {

System.out.println("這是線程中的方法!");

}

}

// 主線程

public static void main(String[] args) {

MyThread mythread = new MyThread();

mythread.start();  // 必須start()啟動線程

for (int i = 0; i < 1000; i++) {

System.out.println("這是主函數的方法!");

}

}

建立線程方法二:實作Runable接口

(1)定義類實作Runable接口

(2)覆寫Runable接口中的run方法

(3)通過Thread類建立線程對象

(4)将Runable接口的子類對象作為實參傳遞給Thread類的構造函數。

(5)調用Thread類的start方法,開啟線程

為什麼要将Runnable接口的子類對象傳遞給Thread的構造函數:

因為,自定義的run方法所屬的對象是Runnable接口的子類對象。

是以要讓線程去指定指定對象的run方法。就必須明确該run方法所屬對象。

例子:

// 自定義建立線程

class MyThread implement Runable {

public void run() {

for (int i = 0; i < 1000; i++) {

System.out.println("這是線程中的方法!");

}

}

// 主線程

public static void main(String[] args) {

MyThread m = new MyThread();

Thread t = new Thread(m);  

m.start()// 必須start()啟動線程

for (int i = 0; i < 1000; i++) {

System.out.println("這是主函數的方法!");

}

}

3 實作方式和繼承方式的差別

實作方式好處:避免了單繼承的局限性。在定義線程時,建議使用實作方式。

兩種方式差別:

繼承Thread:線程代碼存放Thread子類run方法中。

實作Runnable,線程代碼存在接口的子類的run方法。

4 匿名線程

(1)繼承Thread類用匿名内部類實作

new Thread(){

public void run(){

for(int i = 0; i <1000; i++){

System.out.println("這是匿名線程實作,匿名的是線程");

}

}

}.start();

(2)實作Runnable接口用匿名内部類實作

new Thread(new Runnable(){

public void run(){

for (int i = 0; i < 1000; i++) {

System.out.println("匿名線程,匿名的是Runnable!");

}

}

}).start();

例子:

買車票問題:

class Ticket implements Runnable

{

private  int tick = 1000;

Object obj = new Object();

public void run()

{

while(true)

{

synchronized(obj)

{

if(tick>0)

{

//try{Thread.sleep(10);}catch(Exception e){}

System.out.println(Thread.currentThread().getName()+"....sale : "+ tick--);

}

}

}

}

}

class  TicketDemo2

{

public static void main(String[] args) 

{

Ticket t = new Ticket();

Thread t1 = new Thread(t);

Thread t2 = new Thread(t);

Thread t3 = new Thread(t);

Thread t4 = new Thread(t);

t1.start();

t2.start();

t3.start();

t4.start();

}

}

5 Thread類常用方法

currentThread():靜态方法,用來擷取目前線程

getName()、setName():用來擷取、設定目前線程的名字

Sleep(毫秒)控制線程休眠,機關為毫秒

setDeamon(true):将線程設定為守護線程。該線程不會單獨執行。

join:目前線程暫停,等待加入的線程運作結束,目前線程繼續執行

setPriority(int newPriority):更改線程優先級

yield():暫停目前線程對象,執行其他線程

6  多線程的安全問題

當多條語句在操作同一個線程共享資料時,一個線程對多條語句隻執行了一部分,還沒執行完,另一個程序就參與進來執行,導緻共享資料的錯誤。

解決辦法:對多條操作共享資料的語句,隻能讓一個線程執行完。在執行過程中,其他線程不能參與執行。

防止cpu的随機性,由于不同的線程切換,輸出錯亂的結果,是以要對程式進行同步,當進行同步的時候,cpu必須将該線程中的程式全部輸出,執行完全,才會執行别的的程式。

線程同步前提:必須要有兩個或兩個以上的線程;必須是多個線程使用同一個鎖。

              必須保證同步中隻能有一個線程在運作。

同步代碼塊 使用synchronized關鍵字加上一個鎖對象來定義一段代碼

synchronized(鎖對象){}

同步函數  使用synchronized進行修飾。

           非靜态同步方法預設使用目前對象this作為鎖對象。

例子:

public class DemoSynchronized {

public static void main(String[] args) {

Test  t = new Test();

new Thread() {

public void run() {

while(true) {

t.test1();

}

}

}.start();

new Thread() {

public void run() {

while(true) {

t.test2();

}

}

}.start();

}

}

class Test {

public void test1() {

synchronized(this){//鎖對象可以是任意對象,但是要保證鎖對象一緻

System.out.print("一");

System.out.print("二");

System.out.print("三");

System.out.print("四");

}

}

public synchronized void test2() {//同步函數的鎖對象是this

{

System.out.print("1");

System.out.print("2");

System.out.print("3");

System.out.print("4");

}

}

靜态同步函數

同步函數被靜态修飾後,鎖不再是this,因為靜态方法中不可以定義this。

靜态進記憶體時,記憶體中沒有本類對象,但是一定有該類對應的位元組碼檔案*.class.

靜态同步方法的鎖是該方法所在類的位元組碼檔案對象  類名.class

例子:

Class Single

{

  private staticSingles=null;//注意此處 不可以加final來修飾 因為此處s指向是空 如果是final修飾的話表示最終指向空 是不可以的

  private Single(){}

  public static Single getIntance(){

           if(s == null){

   synchronized(Single.class){  //此處是靜态函數 是以用class

   if(s ==null)

    s=new Single();

   }

   }

   returns;

 }

}

------- android教育訓練、java教育訓練、期待與您交流! ----------