t.join():讓主線程進入線程池,等待t執行完才執行。
t.sleep():讓線程阻塞,休眠一段時間,休眠結束后進入就緒狀態。不會釋放鎖。
t.yield():讓線程讓出CPU,從運行態進入就緒態。可能會接着進入運行態。
t.setDaemon():設置為守護線程,非守護線程都死了的時候自動終止。
2 線程的分類
線程分為:守護線程 用戶線程
-
守護線程和用戶線程基本上是相同的,唯一區別就是判斷JVM何時離開
-
守護線程是用來服務用戶線程的。通過在start方法之前調用thread.setDaemon(true) 可以將一個用戶線程變成守護線程
-
java的垃圾回收 他是一個典型的守護線程
-
如果JVM中都是守護線程,JVM將退出(用戶線程執行結束 守護線程無論是否結束 都將終止執行)
3 線程的生命周期
jdk中用Thread State定義了線程的狀態:
-
線程狀態。線程可以處於以下狀態之一:
線程的狀態通常分為5種狀態:
新建:當一個Thread類及其子類的對象被聲明並創建時,此時的線程對象就處於新建狀態
就緒 : 處於新建狀態的線程被start后,線程將進入CPU的執行隊列等待獲得CPU的執行權,此時的線程已經具備了運行的條件,只是還有獲得CPU的執行權
運行:當就緒的線程獲得CPU的執行權 ,處於運行狀態
阻塞:在某種特殊的情況下,被人為掛起或執行輸入輸出操作時,讓出CPU並臨時中止自己的執行。此時線程就進入阻塞狀態
死亡:線程完成了他的全部工作或被線程被提前強制性的終止 或出現異常導致線程異常結束。
線程狀態之間的相互轉換
-
一個線程一旦死亡 是不可以在重新啟動的。
4.線程同步
public class SellTicketDemo {
public static void main(String[] args) { SellTicket st = new SellTicket(); //創建三個線程 Thread t1 = new Thread(st,"1號窗口"); Thread t2 = new Thread(st,"2號窗口"); Thread t3 = new Thread(st,"3號窗口"); //啟動線程 t1.start(); t2.start(); t3.start(); } } public class SellTicket implements Runnable{ private int ticktes = 100; @Override public void run() { while(true){ if(ticktes > 0 ){ try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"張票");//會出現數據不同步的現象,與實際需求不符 ticktes--; } } } }
4.1 解決數據安全問題--同步代碼塊
出現問題的條件:
1 多線程環境
2 有共享數據
3 有多條語句操作共享數據
如何解決線程安全問題:
基本的思想:讓程序沒有安全問題的環境
怎么實現呢? 把多條語句操作共享數據的代碼給鎖起來,讓任意時刻只能有一個線程執行。
java提供了解決的方式是使用同步代碼塊或同步方法:synchronized 相當於給代碼加鎖
可以用在代碼塊和方法上 分別稱為同步代碼塊和同步方法:
代碼塊:synchronized(obj/this){
共享代碼;
}
在同步代碼塊中 誰來擔當這個所對象呢?
任意對象都可以充當所對象 一般情況下使用this
4.2 解決數據安全問題--同步方法
在方法的聲明上添加synchronized關鍵字
同步成員方法
靜態同步方法:static synchronized
靜態方法的同步代碼塊:使用類名.class作為鎖對象
靜態的同步方法或者靜態方法中的同步代碼塊的所對象是類名.class對象
單例設計模式的懶漢式的線程安全問題
public class Singleton {
private static Singleton instance; private Singleton(){ } public static Singleton getInstance(){ if( instance == null){ synchronized (Singleton.class){ if(instance ==null){ instance = new Singleton(); } } } return instance; } }
4.6 線程安全的類
StringBuffer 線程安全的可變字符序列
StringBuilder 線程不安全的可變字符序列
Vector 線程同步的
HashTable 線程同步的 是一個哈希表的實現
在實際使用時,如果不需要線程安全的實現,推薦使用與之功能相同的 但是線程不同步的實現
5 線程的死鎖的演示
死鎖是我們需要規避的問題
不同的線程分別占用對方所需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就行成了線程死鎖
出現死鎖 不會出現異常,不會出現提示,只是所有的線程都處於阻塞狀態 無法繼續
死鎖問題的出現是一個概率時間。
死鎖問題的解決:
1 減少同步代碼塊或同步方法的嵌套
2 盡量減少同步資源的定義
3 使用專門的算法
提示:
1 明確那些代碼是多線程運行的代碼,就是需要寫入run方法
2 明確那些數據是共享數據
3 明確多下稱運行代碼中的那些語句操作了共享數據
6 線程通信
6.1 什么時候需要線程通信
多個線程並發執行,在默認情況下CPU隨機切換線程,如果我們希望他們有規律的執行,就需要使用線程通信。
6.2 線程間如何通信
如果線程需要等待 就調用的wait().wait()方法可以等待時間結束或者被喚醒。如果要喚醒一個等待的線程 那么就使用notify() /notifyAll()
6.3 互斥鎖
互斥鎖 依次最多只能有一個線程持有鎖
鎖是用於通過多個線程控制對共享資源的訪問的工具。 通常,鎖提供對共享資源的獨占訪問:一次只能有一個線程可以獲取鎖,並且對共享資源的所有訪問都要求首先獲取鎖
lock.lock():加鎖 lock.unlock()解鎖
Interface Lock 可以使用的實現類 ReentrantLock
-
一個可重入互斥
Condition
c1.await():使當前線程等待,直到c1.singal()或者interrupt()打斷阻塞狀態,或者到await()指定的時間。
c1.signal():喚醒一個等待線程
c1.signalAll():喚醒所有等待進程
不同的線程需要使用不同的 Condition 這樣就能區分喚醒額時候喚醒的是那個線程
public class ThreadLock {
public static void main(String[] args) { ThreadLock tw = new ThreadLock(); new Thread(new Runnable() { @Override public void run() { while(true){ tw.print1(); } } }).start(); new Thread(){ @Override public void run() { while(true){ tw.print2() ; } } }.start(); new Thread(){ @Override public void run() { while(true){ tw.print3() ; } } }.start(); } // 創建Lock鎖對象 Lock lock = new ReentrantLock(); //創建鎖使用的條件 Condition c1 = lock.newCondition(); Condition c2 = lock.newCondition(); Condition c3 = lock.newCondition(); // 創建一個喚醒標志 int flag = 1; public void print1(){ lock.lock();//給當前代碼上鎖 if(flag != 1){ try { c1.await();//當前線程處於等待 } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print("中"); System.out.print("北"); System.out.print("大"); System.out.print("學"); System.out.println(); flag = 2; c2.signal();//喚醒c2 lock.unlock();//釋放鎖 } public void print2(){ lock.lock(); if(flag != 2){ try { c2.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print("塔"); System.out.print("里"); System.out.print("木"); System.out.print("大"); System.out.print("學"); System.out.println(); flag =3; c3.signal(); lock.unlock(); } public void print3(){ lock.lock(); if(flag != 3){ try { c3.await(); } catch (InterruptedException e) { e.printStackTrace(); } } System.out.print("青"); System.out.print("島"); System.out.print("大"); System.out.print("學"); System.out.println(); flag =1; c1.signal(); lock.unlock(); } }
6.4生產者—消費者模型
生產者消費者模型的作用是什么?
1通過平衡生產者的生產能力和消費者消費能力來提升整個系統的運行效率
2 解耦
多線程的學習重點:
1 線程的創建方式
2線程的生命周期(線程的五種狀態的轉換)
3 線程同步
4 線程通信