1)進程和線程的區別java
進程: 程序運行後,一個QQ,微信等就是一個進程。 線程: 線程是進程中的最小單元。說簡單的話說,線程就是程序中不一樣的執行路徑。 程序: QQ是一個程序,是一個硬盤上的程序,
2)線程run方法和start方法的區別segmentfault
public class T01_WhatIsThread { //新建了靜態內部類繼承Thrread,線程修改1秒,輸入T1 private static class T1 extends Thread { @Override public void run() { for(int i=0; i<10; i++) { try { TimeUnit.MICROSECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("T1"); } } } //main方法 public static void main(String[] args) { new T1().run(); //new T1().start(); for(int i=0; i<10; i++) { try { TimeUnit.MICROSECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("main"); } } } 輸出 T1 T1 main main //這個時候註釋18run方法 開始start方法 執行結果大不同 T1 main T1 main #結論 therad的start方法 執行路徑是分支的形式,而run方法是重上到下依次執行。
3)多線程的經常使用實現方法安全
//線程主要實現的方法有3種 public class T02_HowToCreateThread { //集成Thread的方法 static class MyThread extends Thread { @Override public void run() { System.out.println("Hello MyThread!"); } } //實現Runnable接口 static class MyRun implements Runnable { @Override public void run() { System.out.println("Hello MyRun!"); } } //三種線程不一樣的運行方式 public static void main(String[] args) { new MyThread().start(); new Thread(new MyRun()).start(); //lamda表達式來執行一個線程 new Thread(()->{ System.out.println("Hello Lambda!"); }).start(); } } // lamda表達式也是一種方式,線程池也是一種方式 //Executors.newCachedThreadPool(); //經過線程池去拿到一個線程,而這個線程仍是要執行runable或者start的方法。
4)線程最基本的方法微信
4.1 sleep多線程
4.2 joinide
//測試join線程 static void testJoin() { Thread t1 = new Thread(()->{ //線程1建立了10個線程 for(int i=0; i<10; i++) { System.out.println("A" + i); try { //每一個線程休眠500毫秒 Thread.sleep(500); //TimeUnit.Milliseconds.sleep(500) } catch (InterruptedException e) { e.printStackTrace(); } } }); //線程2 Thread t2 = new Thread(()->{ try { //線程1的線程加入。等待線程1結束 t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } //線程1結束後,纔開始執行此代碼 for(int i=0; i<10; i++) { System.out.println("B" + i); try { Thread.sleep(500); //TimeUnit.Milliseconds.sleep(500) } catch (InterruptedException e) { e.printStackTrace(); } } }); //分別啓動 t1.start(); t2.start(); } #執行結果 A0 A1 A2 A3 A4 A5 A6 A7 A8 A9 B0 B1 B2 B3 B4 B5 B6 B7 B8 B9
4.3 yield學習
對於interrupt的一些說明:測試
#線程經過wait()進入阻塞狀態,此時經過interrupt()中斷該線程; #調用interrupt()會當即將線程的中斷標記設爲「true」,可是因爲線程處於阻塞狀態,因此該「中斷標記」會當即被清除爲「false」,同時,會產生一個InterruptedException的異常。 #咱們會catch這個異常,再根據業務邏輯去處理線程的後續行爲。 #代碼示例 @Override public void run() { try { // 1. isInterrupted()保證,只要中斷標記爲true就終止線程。 while (!isInterrupted()) { // 執行任務... } } catch (InterruptedException ie) { // 2. InterruptedException異常保證,當InterruptedException異常產生時,線程被終止。 } } //interrupt 用於控制業務場景的用法極少,正經常使用法通常是某一個線程阻塞時間很長很長,經過interrupt來打斷線程。
線程狀態的小例子this
static class MyThread extends Thread { @Override public void run() { System.out.println(this.getState()); for(int i=0; i<4; i++) { try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(i); } } } public static void main(String[] args) { Thread t = new MyThread(); System.out.println(t.getState()); t.start(); try { t.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(t.getState()); } #輸出結果 NEW RUNNABLE 0 1 2 3 TERMINATED
須要注意的是 鎖,所的是對象,而不是代碼spa
好比:
public class T { private int count = 10; private Object o = new Object(); public void m() { synchronized(o) { // **任何線程要執行下面的代碼,必須先拿到o的鎖** count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } } } #問題 上述代碼若是new了2個不一樣的object,o和o1,那synchronize(o) 是鎖誰呢,固然鎖o。 ```
synchronize 的幾種鎖的形式
鎖this
private int count = 10; public void m() { synchronized(this) { //任何線程要執行下面的代碼,必須先拿到this的鎖 count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } }
鎖方法
private int count = 10; public synchronized void m() { //等同於在方法的代碼執行時要synchronized(this) count--; System.out.println(Thread.currentThread().getName() + " count = " + count); }
鎖靜態方法
private static int count = 10; public synchronized static void m() { //這裏等同於synchronized(T.class) count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } public static void mm() { synchronized(T.class) { //考慮一下這裏寫synchronized(this)是否能夠?(不可,由於沒有new,) count --; } }
小思考
1) 下面的線程如何輸出? 2)加上synchronize後又有什麼區別 3)加上volatile後又什麼區別
private /*volatile*/ int count = 100; public /*synchronized*/ void run() { count--; System.out.println(Thread.currentThread().getName() + " count = " + count); } public static void main(String[] args) { T t = new T(); for(int i=0; i<100; i++) { new Thread(t, "THREAD" + i).start(); } } #1 打印列會出現重複或者實際減的數和打印的數不一致。 //打印結果抽取異常部分 THREAD81 count = 37 THREAD70 count = 37 #2 synchronize 既保證可見又保證一致性 #3 volatile 保證可見性,這個變量改後立馬被線程發現。 #4 加了synchronize就沒必要加volatile。
$\color{red}{程序若是出現異常,默認狀況下鎖會被釋放}$
public class T { int count = 0; synchronized void m() { System.out.println(Thread.currentThread().getName() + " start"); while(true) { count ++; System.out.println(Thread.currentThread().getName() + " count = " + count); try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } if(count == 5) { int i = 1/0; //此處拋出異常,鎖將被釋放,要想不被釋放,能夠在這裏進行catch,而後讓循環繼續 System.out.println(i); } } } }} public static void main(String[] args) { T t = new T(); Runnable r = new Runnable() { @Override public void run() { t.m(); } }; new Thread(r, "t1").start(); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(r, "t2").start(); } } #//打印結果 人爲異常不補貨,t2獲得鎖。繼續執行 t1 count = 2 t1 count = 3 t1 count = 4 t1 count = 5 t2 start Exception in thread "t1" t2 count = 6 java.lang.ArithmeticException: / by zero at com.mashibing.juc.c_011.T.m(T.java:27) at com.mashibing.juc.c_011.T$1.run(T.java:39) at java.base/java.lang.Thread.run(Thread.java:844) t2 count = 7
推薦文章 鎖升級的小段子
偏向鎖(誰來誰第一,誰偏向)----》
競爭 自旋鎖,(自旋10次或者 JDK目前規定爲自旋線程超過CPU內核數的一半)
若是超過了自旋的上限,就升級重量級鎖,重鎖是OS(操做系統)級別的鎖,並進入等待隊列。
鎖使用的場景:
$\color{red}{線程少,上鎖代碼執行快,用自旋鎖}$ $\color{red}{線程多,上鎖代碼執行長,用OS重量鎖}$