【Java】多執行緒複習
目錄
2.Security Problems in Multithreading(多執行緒)
3.Four kinds of usage of Synchroized(四種修飾)
4.Multithreading Security Problems in (多執行緒安全問題)
Lazy/Hungry Man singleton pattern(單例餓漢/懶漢)
5.Consumer and Producer Problem(生產者-消費者問題)
等待喚醒機制(wait() / notify() / notifyAll())
0.Create a Thread
方法①
繼承Thread類,重寫run()方法,呼叫start方法開啟執行緒(也就是呼叫重寫的run方法)
方法②(常用)
實現Runnable介面,重寫run方法,將實現類作為引數傳遞給Thread物件,最後呼叫start方法開啟執行緒(也就是呼叫run方法)
Demo d = new Demo(); //Demo類實現了Runnable介面,但他還不是執行緒物件
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.start();
t2.start();
Thread原始碼
class Thread{
private Runnable target;
Thread(Runnable target){
this.target = target;
}
public void run(){
if(target != null){
target.run();
}
}
}
**所有的執行緒共享實現類的成員變數
比如Demo中有一個成員變數ticket=100,t1/t2的run方法都可以對其進行操作,而且數值會儲存
兩種建立方式區別
方法①:執行緒任務和執行緒物件耦合在一起,一旦建立了Thread物件,就代表建立了執行緒任務以及執行緒物件,也就是有執行緒任務就有物件了
方法②:執行緒分為兩部分:執行緒物件(Thread物件)+執行緒任務(實現Runnable介面的類),將執行緒任務和執行緒物件解耦,此方式更加物件導向,也就更常用
多執行緒記憶體示意圖
public static void main(String[] args){
Demo d = new Demo();
Thread t1 = new Thread(d);
Thread t2 = new Thread(d);
t1.run(); //由當前執行緒負責
t2.start(); //由新建立的Thread-1負責
}
start和run方法的區別
呼叫run方法不開啟執行緒,僅是物件呼叫方法,由當前執行緒負責方法的執行;
呼叫start方法開啟執行緒,並讓jvm呼叫run方法在新開啟的執行緒中負責方法執行。
1.The Status of Thread
*參考地址:https://www.cnblogs.com/happy-coder/p/6587092.html
1. 新建狀態(New) : 執行緒物件被建立後,就進入了新建狀態。例如,Thread thread = new Thread()。
2. 就緒狀態(Runnable): 也被稱為“可執行狀態”。執行緒物件被建立後,其它執行緒呼叫了該物件的start()方法,從而來啟動該執行緒。例如,thread.start()。處於就緒狀態的執行緒,隨時可能被CPU排程執行。
3. 執行狀態(Running) : 執行緒獲取CPU許可權進行執行。需要注意的是,執行緒只能從就緒狀態進入到執行狀態。
4. 阻塞狀態(Blocked) : 阻塞狀態是執行緒因為某種原因放棄CPU使用權,暫時停止執行。直到執行緒進入就緒狀態,才有機會轉到執行狀態。阻塞的情況分三種:
(01) 等待阻塞 -- 通過呼叫執行緒的wait()方法,讓執行緒等待某工作的完成。
(02) 同步阻塞 -- 執行緒在獲取synchronized同步鎖失敗(因為鎖被其它執行緒所佔用),它會進入同步阻塞狀態。
(03) 其他阻塞 -- 通過呼叫執行緒的sleep()或join()或發出了I/O請求時,執行緒會進入到阻塞狀態。當sleep()狀態超時、join()等待執行緒終止或者超時、或者I/O處理完畢時,執行緒重新轉入就緒狀態。
5. 死亡狀態(Dead) : 執行緒執行完了或者因異常退出了run()方法,該執行緒結束生命週期。
2.Security Problems in Multithreading(多執行緒)
*根本原因也就是對共享資料操作
而要解決此多執行緒安全問題就用到了同步鎖
public void run(){
Object obj = new Object();
synchroized(obj){
//需要同步的程式碼塊,對共享資料的操作
}
}
線上程執行run方法之前,會要求得到物件obj,而obj只有一個。
只要其中一個執行緒A得到了此物件obj,即使CPU切換執行緒B來執行,執行緒B得不到物件obj也就不能執行下去。
而執行緒A在執行完程式碼塊之後,就會將物件obj返回,其他執行緒就可以得到物件obj了。
使用同步鎖弊端:降低了程式的效能(切換程式時得不到物件obj-->不做事)
可能出現的問題:同步中用的不是同一把鎖(物件obj不是同一個),導致同步失效
3.Four kinds of usage of Synchroized(四種修飾)
需要同步的地方
同步裡面一般都是迴圈,如果不是迴圈,瞬間執行完了就沒必要多執行緒了
一般都是同步操作共享資料的程式碼,所以用同步來修飾的地方既不能過大(可能變成單執行緒),也不能過小(執行緒安全問題)
程式碼塊
被修飾的程式碼塊稱為同步語句塊,其作用的範圍是大括號{}括起來的程式碼,作用的物件是呼叫這個程式碼塊的物件(this),可以在括號內指定鎖Synchroized(Obj obj){},鎖是obj
方法
被修飾的方法稱為同步方法,其作用的範圍是整個方法,作用的物件是呼叫這個方法的物件(this)
靜態方法
作用的範圍是整個靜態方法,作用的物件是這個類的所有物件(類.class這個物件)
一個類
其作用的範圍是synchronized後面括號括起來的部分,作用主的物件是這個類的所有物件(類.class這個物件)
4.Multithreading Security Problems in (多執行緒安全問題)
Lazy/Hungry Man singleton pattern(單例餓漢/懶漢)
餓漢(沒有新物件,用getInstance來獲得)
class Single{
private static Single s = new Single();
private Single();
public static Single getInstance(){
return s;
}
}
懶漢(懶得建立新物件,用之前有的)
**多執行緒併發問題:執行緒0判斷完s=null,CPU切換執行權到執行緒1判斷s=null後建立物件s,切換到執行緒0之後又建立物件s
class Single{
private static Single s = null;
private Single();
//加入synchronized解決多執行緒同步問題,但是影響了效率
public static /*synchronized*/ Single getInstance(){
if(s==null){
s = new Single();
return s;
}
}
}
解決方法:
加入synchronized(解決併發問題)
如上述程式碼註釋部分
雙重判斷,減少判斷鎖的次數(解決加同步後效率低下問題)
減少判斷鎖的次數:顧名思義,也就是減少執行synchronized()這一行或synchronized修飾的方法
雙重判斷最核心的地方:第一個執行緒建立完物件之後,其他執行緒不用再判斷鎖(因為在第一個判斷中就已經出去了)
class Single{
private static Single s = null;
private Single();
//雙重判斷
public static Single getInstance(){
if(s==null){
synchronized(Single.class){
if(s==null){
s = new Single();
return s;
}
}
}else return s;
}
}
5.Consumer and Producer Problem(生產者-消費者問題)
背景前瞻
生產者執行緒每生產一個資源,由消費者執行緒消費,然後生產者執行緒再生產……(一個生產者、一個消費者)
class Resource{
private String name;
public synchronized void set(String name) {
this.name = name;
System.out.println(Thread.currentThread().getName() + "生產了" + this.name);
}
public synchronized void out() {
System.out.println(Thread.currentThread().getName() + "消費了" + this.name);
}
}
class Customer implements Runnable{
private Resource r ;
Customer(Resource r){
this.r = r;
}
public void run() {
while(true) {
r.set("麵包");
}
}
}
class Producer implements Runnable{
private Resource r ;
Producer(Resource r){
this.r = r;
}
public void run() {
while(true) {
r.out();
}
}
}
public class PAndC {
public static void main(String[] args) {
Resource r = new Resource();
Producer p1 = new Producer(r);
Customer c1 = new Customer(r);
Thread t1 = new Thread(p1);
Thread t2 = new Thread(c1);
t1.start();
t2.start();
}
}
產生問題
生產者生產多次,才到消費者消費多次
*解決:等待喚醒機制
通過flag標記,如果flag為真(消費者wait(),生產者執行並notify()),如果flag為假(生產者wait(),消費者執行並notify())
class Resource{
private String name;
private boolean flag = false;
public synchronized void set(String name) {
if(flag)try {wait();}catch(Exception e) {};
this.name = name;
System.out.println(Thread.currentThread().getName() + "生產了" + this.name);
flag = true;
notify();
}
public synchronized void out() {
if(!flag)try {wait();}catch(Exception e) {};
System.out.println(Thread.currentThread().getName() + "消費了" + this.name);
flag = false;
notify();
}
}
等待喚醒機制(wait() / notify() / notifyAll())
**這三個方法必須使用在同步(synchronized)中,因為要標識這些方法所屬的鎖,通過鎖來判斷wait()或者notify()哪個執行緒池
wait():將呼叫此方法的執行緒臨時儲存到執行緒池中
notify():會喚醒執行緒池中任一 一個執行緒
notifyAll():會喚醒執行緒池中所有執行緒
*同一個鎖上的notify(),只能喚醒該鎖上的被wait()的執行緒
上述解決方法中,對資源物件(單例)鎖上,呼叫set的程式0,呼叫out的程式1分別臨時儲存到執行緒池中,因為只有一個資源物件,所以可以判斷是同一個執行緒池。
多消費者、多生產者問題
*問題:消費者喚醒的物件可能是消費者,也可能是生產者(我們需要的是喚醒不同方)
喚醒同方會導致生產兩次 or 消費兩次
*解決:while替代if的判斷(多消費者、生產者必備while)
*新問題:替代之後導致死迴圈。eg:生產者喚醒生產者之後,佔著資源,死迴圈(消費者還未將標識變換,本應是喚醒消費者的)
*解決:喚醒所有(notifyAll()),但是效率低,因為又喚醒了本方
*效率低解決辦法:jdk1.5之後提供的java.util.concurrent.locks代替同步
Lock介面:lock()獲得鎖、unlock()釋放鎖 用於代替synchronized
Condition介面:await(),singal(),singalAll() 用於代替wait()、notify()、notifyAll()
*同作業系統中的P/V操作是一個概念,Condition物件就是訊號量
class Resource{
private String name;
private boolean flag = false; //判斷是否需要停止生產者或者消費者的標識
private Lock lock = new ReentrantLock();
private Condition p = lock.newCondition(); //生產
private Condition c = lock.newCondition(); //消費
public void set(String name) {
lock.lock();
try {
while(flag)try {p.await();}catch(Exception e) {};
this.name = name;
System.out.println(Thread.currentThread().getName() + "生產了" + this.name);
flag = true;
c.signal();
}finally {
lock.unlock();
}
}
public void out() {
lock.lock();
try {
while(!flag)try {c.await();}catch(Exception e) {};
System.out.println(Thread.currentThread().getName() + "消費了" + this.name);
flag = false;
p.signal();
}finally {
lock.unlock();
}
}
}
6.Details in Multithreading
sleep()和wait()的異同點
- sleep()必須指定時間;wait()可以指定也可以不指定時間
- sleep()時間到,執行緒處於臨時阻塞或執行狀態;wait()如果沒有時間,需要通過notify()或notifyAll()喚醒
- sleep()不一定要定義在同步中;wait()必須定義在同步中
- 定義在同步中時,執行緒執行sleep(),不會釋放鎖(其他執行緒執行不了);執行緒執行到wait(),會釋放鎖
執行緒如何停止
stop():過時,因為執行之後會釋放其擁有的所有鎖,會引發其他問題,所以被棄用
*interrupt():將執行緒的凍結狀態清除,讓執行緒恢復到正常執行狀態,因為是強制性的所以會自帶異常
**將notify()比喻作喚醒一個睡覺的人;interrupt()就是一棍子打醒睡覺的人,打出來的包就是異常。
執行緒結束:讓run()結束(執行緒處於凍結[wait()]狀態時無法判斷標識flag,沒人喚醒執行緒)
- 方法①:run()中通常都是迴圈,線上程外面控制一個標識來控制迴圈就可以讓執行緒結束
- 方法②:interrupt()打斷之後在catch(){...}中控制標識來控制迴圈來讓執行緒結束
守護(後臺)執行緒
和一般執行緒(前臺)一樣,在啟動執行緒之前需要(thread.setDaemon(true))就可以變成後臺執行緒
*特點:執行緒結束方式:
- 執行完run()方法
- 所有前臺執行緒執行完-->程式執行完
優先順序&執行緒組
預設優先順序5,數字表示1-10,明顯的優先順序1,5,10
serPriority(Thread.MAX_PRIORITY);
執行緒組:可以對多個同組的執行緒,進行統一的操作(比如interrupt())
join() & yield()
t1.join():主執行緒釋放執行權,讓其餘的執行緒搶奪執行權,只有在t1執行完之後主執行緒才能獲得執行權(處於凍結狀態)
yield():釋放CPU執行權,讓其他執行緒有機會獲取(自己也能再次獲取)。讓執行緒放緩,增加間隔性
微妙的地方
new Thread(new Runnable(){
public void run(){
System.out.println("runnable run");
}
}){
public void run(){
System.out.println("subthread run");
}
}.start();
new Thread() {.....}建立了Thread的子類重寫了父類的run方法
父類的run方法原本是:
class Thread{
private Runnable r;
Thread(Runnable r){
this.r = r;
}
public void run(){
if(r != null){
r.run();
}
}
public void start(){
run();
}
}
所以重寫了之後,最後輸出的結果的subthread run
相關文章
- 多執行緒複習執行緒
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- Java多執行緒學習——執行緒通訊Java執行緒
- Java多執行緒學習(2)執行緒控制Java執行緒
- Java多執行緒學習Java執行緒
- Java 多執行緒NIO學習Java執行緒
- Java多執行緒學習(3)執行緒同步與執行緒通訊Java執行緒
- Java多執行緒-執行緒中止Java執行緒
- #大學#Java多執行緒學習02(執行緒同步)Java執行緒
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- Java 多執行緒學習筆記Java執行緒筆記
- Java多執行緒學習(八)執行緒池與Executor 框架Java執行緒框架
- java——多執行緒Java執行緒
- java多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- Java多執行緒學習(1)建立執行緒與執行緒的生命週期Java執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- 多執行緒學習一(多執行緒基礎)執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- Java多執行緒學習筆記(自用)Java執行緒筆記
- java 多執行緒CountDownLatchJava執行緒CountDownLatch
- java 多執行緒-3Java執行緒
- java 多執行緒-2Java執行緒
- java 多執行緒 –同步Java執行緒
- java使用多執行緒Java執行緒
- Java--多執行緒Java執行緒
- java 多執行緒 --同步Java執行緒
- java多執行緒原理Java執行緒