1、線程實現的方式java
一、實現Runnable接口並編寫run()方法併發
二、繼承Thread類並覆蓋run()方法ide
三、前二者都不返回任何值,若是你但願任務在完成時能返回一個值,那麼就須要實現Callable接口.this
import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; public class ThreadTest { public static void main(String[] args) { try { Future<String> result = getDataFromRemote(); System.out.println(result.get()); } catch(InterruptedException | ExecutionException e) { e.printStackTrace(); } } public static Future<String> getDataFromRemote() { ExecutorService service = Executors.newCachedThreadPool(); return service.submit(new Callable<String>() { @Override public String call() throws Exception { System.out.println("getDataFromRemote is ing..."); try { Thread.sleep(2000); } catch(InterruptedException e) { e.printStackTrace(); } return "ok"; } }); } }
//output
getDataFromRemote is ing...
ok
2、一些概念簡介:編碼
Thread.yield():對線程調度器的一種建議,它在聲明:「我已經執行完生命週期中最重要的部分了,此刻正是切換給其餘任務執行一段時間的大好時機」。spa
Thread.sleep():休眠,不釋放對CPU的佔用;Object對象的wait()方法:釋放CPU線程
getPriority():優先級code
後臺線程(daemon):也叫守護線程,是指在程序運行的時候在後臺提供一些通用服務的線程,而且這種線程並不屬於程序中不可或缺的部分。當全部非後臺線程結束,程序被終止,同時進程中的全部後臺線程也會被殺死。典型的就是 垃圾回收。對象
要將一個線程設置爲後臺線程,則調用setDaemon(true)便可、blog
t.join():等待一段時間直到線程t結束,原線程才繼續執行
捕獲異常:因爲線程的本質特性,使得不能捕獲從線程逃逸的異常。一旦異常逃出任務的run()方法後,就會向外傳播到控制檯。除非採起特殊的方式捕獲這些異常。
package threadpool; public class ExceptionThread1 implements Runnable { @Override public void run() { throw new RuntimeException(); } public static void main(String[] args) { try { Thread thread = new Thread(new ExceptionThread1()); thread.start(); } catch(Exception e) { } } } //output Exception in thread "Thread-0" java.lang.RuntimeException at threadpool.ExceptionThread1.run(ExceptionThread1.java:9) at java.lang.Thread.run(Unknown Source)
爲了解決這個問題,java se5以後的新街口Thread.UncaughtExceptionHandler能夠解決這個問題。
package threadpool; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.ThreadFactory; public class ExceptionThread implements Runnable { @Override public void run() { Thread t = Thread.currentThread(); System.out.println("run() by " + t); System.out.println("eh = " + t.getUncaughtExceptionHandler()); throw new RuntimeException(); } public static void main(String[] args) { try { ExecutorService exec = Executors.newCachedThreadPool(new HandlerThreadFactory()); exec.execute(new ExceptionThread()); } catch(Exception e) { e.printStackTrace(); } } } class HandlerThreadFactory implements ThreadFactory { @Override public Thread newThread(Runnable r) { System.out.println(this + " creating new Thread"); Thread t = new Thread(r); System.out.println(" created " + t); t.setUncaughtExceptionHandler(new MyUncaughtExceptionHandler()); System.out.println("eh = " + t.getUncaughtExceptionHandler()); return t; } } class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { System.out.println("caught " + e); } }
//output
threadpool.HandlerThreadFactory@16acdd1 creating new Thread
created Thread[Thread-0,5,main]
eh = threadpool.MyUncaughtExceptionHandler@facf0b
run() by Thread[Thread-0,5,main]
eh = threadpool.MyUncaughtExceptionHandler@facf0b
threadpool.HandlerThreadFactory@16acdd1 creating new Thread
created Thread[Thread-1,5,main]
eh = threadpool.MyUncaughtExceptionHandler@10721b0
caught java.lang.RuntimeException
3、線程同步的方式
所謂死鎖: 是指兩個或兩個以上的進程在執行過程當中,因爲競爭資源或者因爲彼此通訊而形成的一種阻塞的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程稱爲死鎖進程。
死鎖產生的四個條件:
一、Synchronized
全部對象都自動含有單一的鎖(也稱爲監視器)。當在對象上調用其任何synchronzised方法的時候,此對象都被加鎖,這時候該對象的其餘synchronzised方法只有等到前一個方法調用完畢並釋放鎖以後才能被調用。因此,對於某個特定對象來講,其全部synchronzised方法共享同一個鎖。這能夠被用來防止多個任務同時訪問被編碼爲對象內存。
針對每一個類,也有一個鎖。因此synchronized static 方法能夠在類的範圍內防止對static數據的併發訪問。
二、使用顯示的Lock對象
Java SE5的java.util.concurrent類庫還包含有定義在java.util.concurrent.locks中的顯示 互斥機制。Lock對象必須被顯示地建立、鎖定和釋放。所以,它與內建的鎖形式相比,代碼缺少優雅性。可是,對於解決某些類型的問題來講,它更加靈活。
class EvenGenerator extends IntGenerator { private int currentEvenValue = 0; private Lock lock = new ReentrantLock(); @Override public int next() { lock.lock(); try { ++currentEvenValue; Thread.yield(); ++currentEvenValue; return currentEvenValue; } finally { lock.unlock(); } } }
當使用Lock對象時,對lock()的調用,必須放置在finally子句中帶有unlock()的try-finally語句中、
return必須在try子句中,以確保unlock()不會過早發生。
若是使用synchronized,某些事務失敗了,就會拋出一個異常。而又沒法去作任何清理工做,以維護系統使其處於良好狀態。
ReentrantLock容許你嘗試獲取但最終未獲取鎖,這樣若是其餘人已經獲取了這個鎖,那你就能夠決定離開去執行其餘一些事情,而不是等待直至這個鎖被釋放。
lock.tryLock();
lock.tryLock(2, TimeUnit.SECONDS);
4、線程本地存儲--根除對變量的共享
線程本地存儲是一種自動化機制,能夠爲使用相同變量的每一個不一樣的線程都建立不一樣的存儲。
ThreadLocal類實現,一般看成靜態域存儲。在建立ThreadLocal時,你只能經過get()和set()方法來訪問該對象的內容。實質就是每一個單獨的線程都被分配了本身的存儲,由於它們
每一個都須要跟蹤本身的計數值,從而實現了線程間的數據隔離。