1. 程式人生 > 其它 >多執行緒學習小計

多執行緒學習小計

技術標籤:java多執行緒靜態代理

多執行緒複習學習

1. Thread

  1. 繼承Thread類,重寫run()方法,呼叫start開啟執行緒。
package com;

public class demo02 extends Thread{
    @Override
    public void run() {
        //run方法執行緒體
        for (int i = 0; i < 20; i++) {
            System.out.println("run方法執行緒體:"+i);
        }
    }
    public
static void main(String[] args) { //建立執行緒物件 demo02 test = new demo02(); //啟動執行緒 test.start(); for (int i = 0; i < 20; i++) { System.out.println("主執行緒:"+i); } } }
  1. 輸出結果如下,可見兩者交替執行,如果把start更改為run方法,則會先執行執行緒體方法再main方法;
    在這裡插入圖片描述
  2. 注意,執行緒開始不一定立即執行,由cpu排程;
  3. 案例練習
package com;

import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;

//聯絡Thread,實現多執行緒同步下載圖片
public class demo3 extends Thread{
    //網路圖片地址
    private String url;
    //儲存的檔名
    private  String name;

    public demo3(String url, String name)
{ this.url = url; this.name = name; } @Override public void run() { WebDownLoader webDownLoader = new WebDownLoader(); webDownLoader.downloader(url,name); System.out.println("下載了檔名為"+name); } public static void main(String[] args) { demo3 t1 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg1.jpg"); demo3 t2 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg2.jpg"); demo3 t3 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg3.jpg"); t1.start(); t2.start(); t3.start(); } } class WebDownLoader{ //下載方法 public void downloader(String url,String name){ try { FileUtils.copyURLToFile(new URL(url),new File(name)); } catch (IOException e) { e.printStackTrace(); System.out.println("IO異常,downloader方法出問題"); } } }
  1. 結果輸出如下,可見執行緒是同步進行的在這裡插入圖片描述

2. Runnable

  1. 定義類實現runnable介面,實現run方法,編寫執行緒體,然後建立執行緒物件start啟動執行緒;
package com;

public class demo04 implements Runnable {
    @Override
    public void run() {
        //run方法執行緒體
        for (int i = 0; i < 20; i++) {
            System.out.println("run方法執行緒體:"+i);
        }
    }
    public static void main(String[] args) {
        //建立類物件
        demo02 test = new demo02();
        //建立執行緒物件,對比繼承Thread執行緒的不同之處,代理
        Thread thread = new Thread(test);
        //啟動執行緒
        thread.start();

        for (int i = 0; i < 20; i++) {
            System.out.println("主執行緒:"+i);
        }
    }
}
  • 對比繼承Thread類的執行緒,實現Runnable介面的執行緒需要靜態代理,即Thread的物件做代理物件,將Runnable的物件做真實物件;
  1. 對案例進行修改,更改為實現Runnable介面的執行緒,如下:
//除了繼承Thread修改為實現Runnable介面外,只需更改以下程式碼
public static void main(String[] args) {
        demo3 t1 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg1.jpg");
        demo3 t2 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg2.jpg");
        demo3 t3 = new demo3("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg3.jpg");

        new Thread(t1).start();
        new Thread(t2).start();
        new Thread(t3).start();
    }

3. 小結

1.繼承Thread類

  • 子類繼承Thread類具備多執行緒能力;
  • 啟動執行緒:子類物件.start();
  • 不建議使用:避免OOP單繼承侷限性;

2.實現Runnable介面

  • 實現介面Runnable具有多執行緒能力;
  • 啟動執行緒:傳入目標物件+Thread物件.start();
  • 推薦使用:避免單繼承侷限性,靈活方便,方便同一個物件被多個執行緒使用;
  • 相較於繼承Thread,可以做到一份資源多個代理,因為繼承Thread的話啟動是子類物件.start,而實現Runnable介面的是傳入物件實現的,這樣就可以做到多個執行緒操作同一個物件,而不用新建多物件;

3.案例如下

  • 以下案例確實實現了多執行緒操作同一物件,但會出現執行緒不安全的問題,即票拿重複了,這就需要做到執行緒同步才能解決,後續篇章講解。
package com;

public class demo06 implements Runnable{
   //票數
   private int ticketNums=10;
   @Override
   public void run() {
       while (true){
           if (ticketNums<=0){
               break;
           }
           try {
               Thread.sleep(100);
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
           System.out.println(Thread.currentThread().getName()+"拿到了第"+ticketNums--+"張票");
       }
   }

   public static void main(String[] args) {
       demo06 ticket = new demo06();
       new Thread(ticket,"小明").start();
       new Thread(ticket,"老師").start();
       new Thread(ticket,"小紅").start();
   }
}

4. Callable

  1. 實現Callable介面,需要返回值型別;
  2. 重寫call方法,需要丟擲異常;
  3. 建立目標物件;
  4. 建立執行服務;
    ExecutorService ser = Executors.newFixedThreadPool(3);//3個執行緒
  5. 提交執行;
    Future<Boolean> r1 = ser.submit(t1);
  6. 獲取結果;
    boolean rs1 = r1.get();
  7. 關閉服務;
    ser.shutdownNow();

重寫下載圖片案例,實現Callable介面,如下:

package test;

import com.demo3;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.concurrent.*;

public class TestCallable implements Callable<Boolean> {
    //網路圖片地址
    private String url;
    //儲存的檔名
    private  String name;

    public TestCallable(String url, String name) {
        this.url = url;
        this.name = name;
    }
    @Override
    public Boolean call() {
        WebDownLoader webDownLoader = new WebDownLoader();
        webDownLoader.downloader(url,name);
        System.out.println("下載了檔名為"+name);
        return true;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TestCallable t1 = new TestCallable("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg1.jpg");
        TestCallable t2 = new TestCallable("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg2.jpg");
        TestCallable t3 = new TestCallable("http://img.ewebweb.com/uploads/20190623/18/1561285221-LqWIvRregC.jpg","jpg3.jpg");

        //建立執行服務
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交執行
        Future<Boolean> r1 = ser.submit(t1);
        Future<Boolean> r2 = ser.submit(t2);
        Future<Boolean> r3 = ser.submit(t3);
        //獲取結果
        boolean rs1 = r1.get();
        boolean rs2 = r2.get();
        boolean rs3 = r3.get();
        //關閉服務
        ser.shutdownNow();
    }
}
class WebDownLoader{
    //下載方法
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO異常,downloader方法出問題");
        }
    }
}
  1. 相較於實現Runnable,Callable有如下好處:
  • 可以定義返回值;
  • 可以丟擲異常;