Java多線程的實現

Java多線程的實現java

用多線程只有一個目的:更好的利用cpu資源.燒水的例子.(當洗杯子花5分鐘,線程要停5分鐘等待返回結果才能進行後續的燒水操做,新開一個線程執行洗杯子操做)。web

1、關於線程的一些概念算法

  • cpu時間片:咱們操做系統看起來能夠多個程序同時運行.分時操做系統,將時間分紅長短相同的時間區域,分配給一個線程使用,當線程尚未結束,時間片已通過去,該線程只有先中止,等待下一個時間片.cpu運行很快,中間的停頓感受不出來.
  • 多線程:指的是這個程序(一個進程)運行時產生了不止一個線程(好比,下載程序,開啓多個線程同時進行.)
  • 並行:多個cpu實例或者多臺機器同時執行一段處理邏輯,是真正的同時。
  • 併發:經過cpu調度算法,讓用戶看上去同時執行,實際上從cpu操做層面不是真正的同時。併發每每在場景中有公用的資源,那麼針對這個公用的資源每每產生瓶頸,咱們會用TPS或者QPS來反應這個系統的處理能力.
  • 線程安全:常常用來描繪一段代碼。指在併發的狀況之下,該代碼通過多線程使用,線程的調度順序不影響任何結果。這個時候使用多線程,咱們只須要關注系統的內存,cpu是否是夠用便可。反過來,線程不安全就意味着線程的調度順序會影響最終結果.
  • Java中的同步指的是經過人爲的控制和調度,保證共享資源的多線程訪問成爲線程安全,來保證結果的準確。如上面的代碼簡單加入@synchronized關鍵字。在保證結果準確的同時,提升性能,纔是優秀的程序。線程安全的優先級高於性能.

2、Java多線程的實現安全

一、繼承Thread類建立線程多線程

Thread類本質上是實現了Runnable接口,啓動該線程的惟一方法是start()方法,併發

public class MyThread extends Thread{
    //普通的調用方法,定義任務要完成的工做.
    @Override
    public void run() {
        System.out.println("新線程正在執行,處理相關的邏輯!");
    }
}


public class Test {
    public static void main(String[] args) {
        //實例化對象
        MyThread myThread1 = new MyThread();  
        MyThread myThread2 = new MyThread();    
        //開啓新的線程,分配新的資源
        myThread1.start();
        myThread2.start();
    }
}

二、實現Runnable接口建立線程框架

java中是單繼承的,若是繼承了一個類,就不能直接繼承Thread類,須要實現Runnable接口的方式達到開啓新線程的目的.異步

public class MyThread implements Runnable {
    //普通的調用方法,定義任務要完成的工做.
    @Override
    public void run() {
        System.out.println("新線程正在執行,處理相關的邏輯!");
    }    
 }
 
 
 public class Test {
    public static void main(String[] args) {
         MyThread myThread = new MyThread();  
        Thread thread =new Thread(myThread);
        //開啓新的線程,分配新的資源
        thread.start();
    }
}

三、實現Callable接口ide

Callable接口的call()方法相似run()方法,都是定義任務要完成的工做.主要不一樣點是call()方法是有返回值的、能夠拋出異常。Callable類型的任務能夠有兩種方法開啓執行.性能

方法一:藉助FutureTask執行(FutureTask、Callable)

將Callable接口對象放到FutureTask對象中,FutureTask的get()方法,能夠獲取返回值.

public class MyCallableTask  implements Callable<Integer> {
    @Override
    public Integer call() throws Exception {
        System.out.println("新線程正在執行,處理相關的邏輯!");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++) {
            sum += i;
        }
        return sum;
    }    
}

    
public class Test {
    public static void main(String[] args) {
        Callable<Integer> mycallabletask = new MyCallableTask();   
        //由Callable<Integer>建立一個FutureTask<Integer>對象:   
        FutureTask<Integer> futuretask = new FutureTask<Integer>(mycallabletask);   
        //註釋:FutureTask<Integer>是一個包裝器,它經過接受Callable<Integer>來建立,它同時實現了Future            和Runnable接口。 
        //由FutureTask<Integer>建立一個Thread對象:   
        Thread oneThread = new Thread(futuretask);
        oneThread.start();
        try {
            //經過futuretask中get()方法能夠獲得MyCallableTask的call()運行結果.
            //須要使用時獲取出來,不然出現堵塞,本線程要等待新線程執行完返回結果才執行
            Integer i = futuretask.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

方法二:藉助線程池來運行 (ExecutorService、Callable、Future)

ExecutorService、Callable、Future三個接口實際上都是屬於Executor框架。

執行Callable任務後,能夠獲取一個Future的對象,在該對象上調用get()就能夠獲取到Callable任務返回的Object了。

public class MyCallableTask  implements Callable<Integer> {    
    @Override
    public Integer call() throws Exception {
        System.out.println("新線程正在執行,處理相關的邏輯!");
        Thread.sleep(3000);
        int sum = 0;
        for(int i=0;i<100;i++) {
            sum += i;
        }
        return sum;
    }
}

public class Test {
    public static void main(String[] args) {
        int taskSize = 5;
        //建立線程池
        ExecutorService threadPool = Executors.newCachedThreadPool(taskSize);
        //提交一個Callable任務,返回一個Future類型
        Future<Integer> future = threadPool.submit(new MyCallableTask());
         try {
            Thread.sleep(3000);//模擬本線程的一些任務
            //獲取執行結果get方法是阻塞的
            System.out.println(future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }    
    }
}

採用匿名類直接新建Callable接口

public class Test{
    public static void main(String[] args) {
        // 建立線程池
        ExecutorService threadPool = Executors.newCachedThreadPool();
        // 提交一個Callable任務,返回一個Future類型
        Future<Integer> future = threadPool.submit(new Callable<Integer>() {
            @Override
            public Integer call() throws Exception {
                System.out.println("新線程正在執行,處理相關的邏輯!");
                Thread.sleep(3000);
                int sum = 0;
                for (int i = 0; i < 100; i++) {
                    sum += i;
                }
                return sum;
            }
        });

        try {
            Thread.sleep(3000);//模擬本線程的一些任務
            //獲取執行結果
            System.out.println(future.get());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

3、使用場景

1、Tomcat內部採用了多線程,上百個用戶同時訪問同一個web應用,都會新開一個線程,調用到Servlet程序。若是不使用多線程,將串行操做,客戶端將等待別人執行完才能訪問。

2、異步請求,有兩個任務Task a和Task b,單線程只能先進行a再進行b。

3、須要知道執行進度,好比說咱們常看到的進度條,任務執行到必定進度給new 一個變量,給變量+1.新開一個線程去輪詢這個變量,反饋給客戶端,這樣就能夠看到進度狀況.

總之,不少地方都用到了多線程,多線程是爲了充分利用cpu資源,當你發現一個業務邏輯執行效率特別低,耗時長,能夠考慮使用多線程.

相關文章
相關標籤/搜索