Java多線程的實現java
用多線程只有一個目的:更好的利用cpu資源.燒水的例子.(當洗杯子花5分鐘,線程要停5分鐘等待返回結果才能進行後續的燒水操做,新開一個線程執行洗杯子操做)。web
1、關於線程的一些概念算法
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資源,當你發現一個業務邏輯執行效率特別低,耗時長,能夠考慮使用多線程.