現代操做系統調度的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程裏能夠建立多個線程,這些線程都擁有各自的計數器、堆棧和局部變量等屬性,而且可以訪問共享的內存變量。html
java.lang.Thread.State
中定義了 6 種不一樣的線程狀態,在給定的一個時刻,線程只能處於其中的一個狀態。java
如下是各狀態的說明,以及狀態間的聯繫:git
start()
方法的線程處於此狀態。start()
方法的線程狀態。此狀態意味着,線程已經準備好了,一旦被線程調度器分配了 CPU 時間片,就能夠運行線程。Object.wait()
以後輸入同步塊/方法或從新輸入同步塊/方法。Object.wait()
Thread.join()
LockSupport.park()
Thread.sleep(sleeptime)
Object.wait(timeout)
Thread.join(timeout)
LockSupport.parkNanos(timeout)
LockSupport.parkUntil(timeout)
run()
方法執行結束,或者因異常退出了 run()
方法,則該線程結束生命週期。死亡的線程不可再次復生。構造線程主要有三種方式程序員
Thread
類Runnable
接口Callable
接口經過繼承 Thread 類構造線程的步驟:github
示例:數據庫
public class ThreadDemo02 { public static void main(String[] args) { Thread02 mt1 = new Thread02("線程A "); // 實例化對象 Thread02 mt2 = new Thread02("線程B "); // 實例化對象 mt1.start(); // 調用線程主體 mt2.start(); // 調用線程主體 } static class Thread02 extends Thread { private int ticket = 5; Thread02(String name) { super(name); } @Override public void run() { for (int i = 0; i < 100; i++) { if (this.ticket > 0) { System.out.println(this.getName() + " 賣票:ticket = " + ticket--); } } } } }
經過實現 Runnable 接口構造線程的步驟:安全
示例:網絡
public class RunnableDemo { public static void main(String[] args) { MyThread t = new MyThread("Runnable 線程"); // 實例化對象 new Thread(t).run(); // 調用線程主體 new Thread(t).run(); // 調用線程主體 new Thread(t).run(); // 調用線程主體 } static class MyThread implements Runnable { private int ticket = 5; private String name; MyThread(String name) { this.name = name; } @Override public void run() { for (int i = 0; i < 100; i++) { if (this.ticket > 0) { System.out.println(this.name + " 賣票:ticket = " + ticket--); } } } } }
經過實現 Callable 接口構造線程的步驟:併發
示例:dom
public class CallableAndFutureDemo { public static void main(String[] args) { Callable<Integer> callable = () -> new Random().nextInt(100); FutureTask<Integer> future = new FutureTask<>(callable); new Thread(future).start(); try { Thread.sleep(1000);// 可能作一些事情 System.out.println(future.get()); } catch (InterruptedException | ExecutionException e) { e.printStackTrace(); } } }
當一個線程運行時,另外一個線程能夠直接經過 interrupt()
方法中斷其運行狀態。
public class ThreadInterruptDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 實例化Runnable子類對象 Thread t = new Thread(mt, "線程"); // 實例化Thread對象 t.start(); // 啓動線程 try { Thread.sleep(2000); // 線程休眠2秒 } catch (InterruptedException e) { System.out.println("三、休眠被終止"); } t.interrupt(); // 中斷線程執行 } static class MyThread implements Runnable { @Override public void run() { System.out.println("一、進入run()方法"); try { Thread.sleep(10000); // 線程休眠10秒 System.out.println("二、已經完成了休眠"); } catch (InterruptedException e) { System.out.println("三、休眠被終止"); return; // 返回調用處 } System.out.println("四、run()方法正常結束"); } } }
Thread 中的 stop 方法有缺陷,已廢棄。
安全地終止線程有兩種方法:
public class ThreadStopDemo03 { public static void main(String[] args) throws Exception { MyTask one = new MyTask(); Thread countThread = new Thread(one, "CountThread"); countThread.start(); // 睡眠1秒,main線程對CountThread進行中斷,使CountThread可以感知中斷而結束 TimeUnit.SECONDS.sleep(1); countThread.interrupt(); MyTask two = new MyTask(); countThread = new Thread(two, "CountThread"); countThread.start(); // 睡眠1秒,main線程對Runner two進行取消,使CountThread可以感知on爲false而結束 TimeUnit.SECONDS.sleep(1); two.cancel(); } private static class MyTask implements Runnable { private long i; private volatile boolean on = true; @Override public void run() { while (on && !Thread.currentThread().isInterrupted()) { i++; } System.out.println("Count i = " + i); } void cancel() { on = false; } } }
run
- 線程的執行實體。start
- 線程的啓動方法。setName
、getName
- 能夠經過 setName()、 getName() 來設置、獲取線程名稱。setPriority
、getPriority
- 在 Java 中,全部線程在運行前都會保持在就緒狀態,那麼此時,哪一個線程優先級高,哪一個線程就有可能被先執行。能夠經過 setPriority、getPriority 來設置、獲取線程優先級。setDaemon
、isDaemon
- 可使用 setDaemon() 方法設置線程爲守護線程;可使用 isDaemon() 方法判斷線程是否爲守護線程。isAlive
- 能夠經過 isAlive 來判斷線程是否啓動。interrupt
- 當一個線程運行時,另外一個線程能夠直接經過 interrupt() 方法中斷其運行狀態。join
- 使用 join() 方法讓一個線程強制運行,線程強制運行期間,其餘線程沒法運行,必須等待此線程完成以後才能夠繼續執行。Thread.sleep
- 使用 Thread.sleep() 方法便可實現休眠。Thread.yield
- 可使用 Thread.yield() 方法將一個線程的操做暫時讓給其餘線程執行。在 Thread 類中能夠經過 setName()
、 getName()
來設置、獲取線程名稱。
public class ThreadNameDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 實例化Runnable子類對象 new Thread(mt).start(); // 系統自動設置線程名稱 new Thread(mt, "線程-A").start(); // 手工設置線程名稱 Thread t = new Thread(mt); // 手工設置線程名稱 t.setName("線程-B"); t.start(); } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "運行,i = " + i); // 取得當前線程的名字 } } } }
在 Thread 類中能夠經過 isAlive()
來判斷線程是否啓動。
public class ThreadAliveDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 實例化Runnable子類對象 Thread t = new Thread(mt, "線程"); // 實例化Thread對象 System.out.println("線程開始執行以前 --> " + t.isAlive()); // 判斷是否啓動 t.start(); // 啓動線程 System.out.println("線程開始執行以後 --> " + t.isAlive()); // 判斷是否啓動 for (int i = 0; i < 3; i++) { System.out.println(" main運行 --> " + i); } // 如下的輸出結果不肯定 System.out.println("代碼執行以後 --> " + t.isAlive()); // 判斷是否啓動 } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 3; i++) { System.out.println(Thread.currentThread().getName() + "運行,i = " + i); } } } }
在 Java 程序中,只要前臺有一個線程在運行,則整個 Java 進程就不會消失,因此此時能夠設置一個守護線程,這樣即便 Java 進程結束了,此守護線程依然會繼續執行。可使用 setDaemon()
方法設置線程爲守護線程;可使用 isDaemon()
方法判斷線程是否爲守護線程。
public class ThreadDaemonDemo { public static void main(String[] args) { Thread t = new Thread(new MyThread(), "線程"); t.setDaemon(true); // 此線程在後臺運行 System.out.println("線程 t 是不是守護進程:" + t.isDaemon()); t.start(); // 啓動線程 } static class MyThread implements Runnable { @Override public void run() { while (true) { System.out.println(Thread.currentThread().getName() + "在運行。"); } } } }
在 Java 中,全部線程在運行前都會保持在就緒狀態,那麼此時,哪一個線程優先級高,哪一個線程就有可能被先執行。
public class ThreadPriorityDemo { public static void main(String[] args) { System.out.println("主方法的優先級:" + Thread.currentThread().getPriority()); System.out.println("MAX_PRIORITY = " + Thread.MAX_PRIORITY); System.out.println("NORM_PRIORITY = " + Thread.NORM_PRIORITY); System.out.println("MIN_PRIORITY = " + Thread.MIN_PRIORITY); Thread t1 = new Thread(new MyThread(), "線程A"); // 實例化線程對象 Thread t2 = new Thread(new MyThread(), "線程B"); // 實例化線程對象 Thread t3 = new Thread(new MyThread(), "線程C"); // 實例化線程對象 t1.setPriority(Thread.MIN_PRIORITY); // 優先級最低 t2.setPriority(Thread.MAX_PRIORITY); // 優先級最低 t3.setPriority(Thread.NORM_PRIORITY); // 優先級最低 t1.start(); // 啓動線程 t2.start(); // 啓動線程 t3.start(); // 啓動線程 } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(500); // 線程休眠 } catch (InterruptedException e) { e.printStackTrace(); } // 取得當前線程的名字 String out = Thread.currentThread().getName() + ",優先級:" + Thread.currentThread().getPriority() + ",運行:i = " + i; System.out.println(out); } } } }
wait、notify、notifyAll 是 Object 類中的方法。
wait
- 線程自動釋放其佔有的對象鎖,並等待 notify。notify
- 喚醒一個正在 wait 當前對象鎖的線程,並讓它拿到對象鎖。notifyAll
- 喚醒全部正在 wait 前對象鎖的線程。生產者、消費者示例:
public class ThreadWaitNotifyDemo02 { private static final int QUEUE_SIZE = 10; private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE); public static void main(String[] args) { new Producer("生產者A").start(); new Producer("生產者B").start(); new Consumer("消費者A").start(); new Consumer("消費者B").start(); } static class Consumer extends Thread { Consumer(String name) { super(name); } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == 0) { try { System.out.println("隊列空,等待數據"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notifyAll(); } } queue.poll(); // 每次移走隊首元素 queue.notifyAll(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 從隊列取走一個元素,隊列當前有:" + queue.size() + "個元素"); } } } } static class Producer extends Thread { Producer(String name) { super(name); } @Override public void run() { while (true) { synchronized (queue) { while (queue.size() == QUEUE_SIZE) { try { System.out.println("隊列滿,等待有空餘空間"); queue.wait(); } catch (InterruptedException e) { e.printStackTrace(); queue.notifyAll(); } } queue.offer(1); // 每次插入一個元素 queue.notifyAll(); try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + " 向隊列取中插入一個元素,隊列當前有:" + queue.size() + "個元素"); } } } } }
在線程操做中,可使用 Thread.yield()
方法將一個線程的操做暫時讓給其餘線程執行。
public class ThreadYieldDemo { public static void main(String[] args) { MyThread t = new MyThread(); new Thread(t, "線程A").start(); new Thread(t, "線程B").start(); } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1000); } catch (Exception e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "運行,i = " + i); if (i == 2) { System.out.print("線程禮讓:"); Thread.yield(); } } } } }
在線程操做中,可使用 join()
方法讓一個線程強制運行,線程強制運行期間,其餘線程沒法運行,必須等待此線程完成以後才能夠繼續執行。
public class ThreadJoinDemo { public static void main(String[] args) { MyThread mt = new MyThread(); // 實例化Runnable子類對象 Thread t = new Thread(mt, "mythread"); // 實例化Thread對象 t.start(); // 啓動線程 for (int i = 0; i < 50; i++) { if (i > 10) { try { t.join(); // 線程強制運行 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.println("Main 線程運行 --> " + i); } } static class MyThread implements Runnable { @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println(Thread.currentThread().getName() + " 運行,i = " + i); // 取得當前線程的名字 } } } }
直接使用 Thread.sleep()
方法便可實現休眠。
public class ThreadSleepDemo { public static void main(String[] args) { new Thread(new MyThread("線程A", 1000)).start(); new Thread(new MyThread("線程A", 2000)).start(); new Thread(new MyThread("線程A", 3000)).start(); } static class MyThread implements Runnable { private String name; private int time; private MyThread(String name, int time) { this.name = name; // 設置線程名稱 this.time = time; // 設置休眠時間 } @Override public void run() { try { Thread.sleep(this.time); // 休眠指定的時間 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(this.name + "線程,休眠" + this.time + "毫秒。"); } } }
ThreadLocal,不少地方叫作線程本地變量,也有些地方叫作線程本地存儲,其實意思差很少。可能不少朋友都知道 ThreadLocal 爲變量在每一個線程中都建立了一個副本,那麼每一個線程能夠訪問本身內部的副本變量。
ThreadLocal 的主要方法:
public class ThreadLocal<T> { public T get() {} public void remove() {} public void set(T value) {} public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {} }
get() 源碼實現
get 源碼
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
ThreadLocalMap 源碼實現
ThreadLocalMap 源碼
ThreadLocalMap 是 ThreadLocal 的一個內部類。
ThreadLocalMap 的 Entry 繼承了 WeakReference,而且使用 ThreadLocal 做爲鍵值。
setInitialValue 源碼實現
private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
若是 map 不爲空,就設置鍵值對;爲空,再建立 Map,看一下 createMap 的實現:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
ThreadLocal 源碼小結
至此,可能大部分朋友已經明白了 ThreadLocal 是如何爲每一個線程建立變量的副本的:
ThreadLocal 最多見的應用場景爲用於解決數據庫鏈接、Session 管理等問題。
示例 - 數據庫鏈接
private static ThreadLocal<Connection> connectionHolder = new ThreadLocal<Connection>() { public Connection initialValue() { return DriverManager.getConnection(DB_URL); } }; public static Connection getConnection() { return connectionHolder.get(); }
示例 - Session 管理
private static final ThreadLocal threadSession = new ThreadLocal(); public static Session getSession() throws InfrastructureException { Session s = (Session) threadSession.get(); try { if (s == null) { s = getSessionFactory().openSession(); threadSession.set(s); } } catch (HibernateException ex) { throw new InfrastructureException(ex); } return s; }
管道輸入/輸出流和普通的文件輸入/輸出流或者網絡輸入/輸出流不一樣之處在於,它主要用於線程之間的數據傳輸,而傳輸的媒介爲內存。 管道輸入/輸出流主要包括了以下 4 種具體實現:PipedOutputStream、PipedInputStream、PipedReader 和 PipedWriter,前兩種面向字節,然後兩種面向字符。
public class Piped { public static void main(String[] args) throws Exception { PipedWriter out = new PipedWriter(); PipedReader in = new PipedReader(); // 將輸出流和輸入流進行鏈接,不然在使用時會拋出IOException out.connect(in); Thread printThread = new Thread(new Print(in), "PrintThread"); printThread.start(); int receive = 0; try { while ((receive = System.in.read()) != -1) { out.write(receive); } } finally { out.close(); } } static class Print implements Runnable { private PipedReader in; Print(PipedReader in) { this.in = in; } public void run() { int receive = 0; try { while ((receive = in.read()) != -1) { System.out.print((char) receive); } } catch (IOException e) { e.printStackTrace(); } } } }
run() 方法是線程的執行體。
start() 方法會啓動線程,而後 JVM 會讓這個線程去執行 run() 方法。
能夠直接調用 Thread 類的 run() 方法麼?
參考閱讀:Java 線程中 yield 與 join 方法的區別 參考閱讀:sleep(),wait(),yield()和 join()方法的區別
thread.setPriority(Thread.MAX_PRIORITY)
的方式設置,默認優先級爲 5。參考閱讀:Java 中守護線程的總結
Java 的每一個對象中都有一個鎖(monitor,也能夠成爲監視器) 而且 wait(),notify()等方法用於等待對象的鎖或者通知其餘線程對象的監視器可用。在 Java 的線程中並無可供任何對象使用的鎖和同步器。這就是爲何這些方法是 Object 類的一部分,這樣 Java 的每個類都有用於線程間通訊的基本方法
當一個線程須要調用對象的 wait()方法的時候,這個線程必須擁有該對象的鎖,接着它就會釋放這個對象鎖並進入等待狀態直到其餘線程調用這個對象上的 notify()方法。一樣的,當一個線程須要調用對象的 notify()方法時,它會釋放這個對象的鎖,以便其餘在等待的線程就能夠獲得這個對象鎖。因爲全部的這些方法都須要線程持有對象的鎖,這樣就只能經過同步來實現,因此他們只能在同步方法或者同步塊中被調用。
免費Java資料須要本身領取,涵蓋了Java、Redis、MongoDB、MySQL、Zookeeper、Spring Cloud、Dubbo/Kafka、Hadoop、Hbase、Flink等高併發分佈式、大數據、機器學習等技術。
傳送門: https://mp.weixin.qq.com/s/JzddfH-7yNudmkjT0IRL8Q