執行緒同步:解決執行緒不安全問題
當多個執行緒併發訪問同一個資源物件時,可能會出現執行緒不安全的問題,比如現有50個蘋果,現在有請三個童鞋(小A,小B,小C)上臺表演吃蘋果.因為A,B,C三個人可以同時吃蘋果,此時使用多執行緒技術來實現這個案例.
class Apple implements Runnable{ private int num = 50;//蘋果總數 public void run() { for (int i = 0; i < 50; i++) { if (num > 0) { try { //模擬網路延遲 Thread.sleep(100); System.out.println(Thread.currentThread().getName() + "吃了編號為" + num-- + "的蘋果"); } catch (Exception e) { e.printStackTrace(); } } } } } public class AppleEatingImplements { public static void main(String[] args) { //建立三個執行緒(三個同學),吃蘋果 Runnable apple = new Apple(); new Thread(apple,"A童鞋").start(); new Thread(apple,"B童鞋").start(); new Thread(apple,"C童鞋").start(); } }
以上程式碼執行結果:
為什麼編號為39的蘋果被吃了兩次呢?
當A、B執行緒拿到編號為39的蘋果時,打印出來,有一個還沒來得及做num--,而有一個做了num減一操作,num還剩38,這時候執行緒進入睡眠狀態。這時候C執行緒來了,列印38,做減1操作,睡眠……
要解決上述多執行緒併發訪問多一個資源的安全性問題,就必須得保證列印蘋果編號和蘋果總數減1操作,必須同步完成.即是說,A執行緒進入操作的時候,B和C執行緒只能在外等著,A操作結束,A和B和C才有機會進入程式碼去執行.
解決多執行緒併發訪問資源的安全問題,有三種方式:
方式1:同步程式碼塊
方式2:同步方法
方式3:鎖機制(Lock)
方式
語法:
synchronized(同步鎖)
{
需要同步操作的程式碼
}
同步鎖:
為了保證每個執行緒都能正常執行原子操作,Java引入了執行緒同步機制.也稱為同步監聽物件/同步鎖/同步監聽器/互斥鎖。
實際上,物件的同步鎖只是一個概念,可以想象為在物件上標記了一個鎖,誰拿到鎖,誰就可以進入程式碼塊,其他執行緒只能在程式碼塊外面等著,而且注意,在任何時候,最多允許一個執行緒擁有同步鎖.
Java程式執行可以使用任何物件作為同步監聽物件,但是一般的,我們把當前併發訪問的共同資源作為同步監聽物件.
//同步程式碼塊 class Apple2 implements Runnable{ private int num = 100; public void run() { for(int i = 0; i < 50; i ++){ //this表示Apple2物件,該物件屬於多執行緒共享的資源 synchronized(this){ if(num>0){ System.out.println(Thread.currentThread().getName()+"吃了"+num-- +"個蘋果"); } } } } } public class SynchronizedBlockDemo { public static void main(String[] args) { Runnable a = new Apple2(); //三個執行緒表示三個人 new Thread(a,"小A").start(); new Thread(a,"小B").start(); new Thread(a,"小C").start(); } }
方式2:同步方法:
使用synchronized修飾的方法,就叫做同步方法,保證A執行緒執行該方法的時候,其他執行緒只能在方法外等著.
Synchronized public void doWork(){
///TODO
}
同步鎖是誰:
對於非static方法,同步鎖就是this.
對於static方法,我們使用當前方法所在類的位元組碼物件(Apple2.class).
//同步方法
class Apple3 implements Runnable{
<span style="white-space:pre"> </span>private int num = 50;
<span style="white-space:pre"> </span>public void run() {
<span style="white-space:pre"> </span>for(int i = 0; i < 50; i ++){
<span style="white-space:pre"> </span>try {
<span style="white-space:pre"> </span>doWork();
<span style="white-space:pre"> </span>} catch (InterruptedException e) {
<span style="white-space:pre"> </span>// TODO Auto-generated catch block
<span style="white-space:pre"> </span>e.printStackTrace();
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>synchronized private void doWork() throws InterruptedException {
<span style="white-space:pre"> </span>if(num>0){
<span style="white-space:pre"> </span>System.out.println(Thread.currentThread().getName()+"吃了"+num +"個蘋果");
<span style="white-space:pre"> </span>num --;
<span style="white-space:pre"> </span>Thread.sleep(10);
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>}
}
public class SynchronizedMethodDemo {
<span style="white-space:pre"> </span>public static void main(String[] args) {
<span style="white-space:pre"> </span>Runnable a = new Apple3();
<span style="white-space:pre"> </span>new Thread(a,"小A").start();//三個執行緒表示三個人
<span style="white-space:pre"> </span>new Thread(a,"小B").start();
<span style="white-space:pre"> </span>new Thread(a,"小C").start();
<span style="white-space:pre"> </span>
<span style="white-space:pre"> </span>}
}
注意:
不要使用synchronized修飾run方法,修飾之後,某一個執行緒就執行完了所有的功能. 好比是多個執行緒出現序列.
解決方案:把需要同步操作的程式碼定義在一個新的方法中,並且該方法使用synchronized修飾,再在run方法中呼叫該新的方法即可.
實際上,同步程式碼塊和同步方法差不了多少,在本質上是一樣的,兩者都用了一個關鍵字synchronized,synchronized保證了多執行緒併發訪問時的同步操作,避免執行緒的安全性問題,但是有一個弊端,就是使用synchronized的方法/程式碼塊的效能比不用要低一些,因此如果要用synchronized,建議儘量減小synchronized的作用域。
方式3:同步鎖(鎖機制)
Lock機制提供了比synchronized程式碼塊和synchronized方法更廣泛的鎖定操作,同步程式碼塊/同步方法具有的功能Lock都有,除此之外更強大,更體現面向物件.
//鎖機制
class Apple4 implements Runnable{
private int num = 50;
//建立鎖物件
private final Lock lock = new ReentrantLock();
public void run() {
for(int i = 0; i < 50; i ++){
doWork();
}
}
private void doWork() {
//進入方法,立馬加鎖
lock.lock();//獲取鎖
try {
//注意:if要放到try裡,不然num為0時就不進入if中,最後鎖就釋放不了了
if(num>0){
System.out.println(Thread.currentThread().getName()+"吃了"+num +"個蘋果");
num--;
Thread.sleep(10);
}
} catch (Exception e) {
e.printStackTrace();
}finally{
//釋放鎖
lock.unlock();
}
}
}
public class LockDemo {
public static void main(String[] args) {
Runnable a = new Apple4();
new Thread(a,"小A").start();//三個執行緒表示三個人
new Thread(a,"小B").start();
new Thread(a,"小C").start();
}
}
相關推薦
執行緒同步:解決執行緒不安全問題
當多個執行緒併發訪問同一個資源物件時,可能會出現執行緒不安全的問題,比如現有50個蘋果,現在有請三個童鞋(小A,小B,小C)上臺表演吃蘋果.因為A,B,C三個人可以同時吃蘋果,此時使用多執行緒技術來實
C# 基礎(十四)C#單例模式:首先介紹 單執行緒、多執行緒、加鎖 單例模式。然後介紹單例模式的執行緒同步:多執行緒有序訪問共享記憶體。
一、簡介 本篇文章將介紹如何使用單例模式,也就是類的例項化,在整個專案的生命週期內,只例項化一次。在單例模式中,往往可以看到如SourceCode.cs:這樣的結構的。 SourceCode.cs: public class Singleton { private static
多執行緒同步:基於棧式同步
什麼是基於棧式同步?這是一種完全基於非同步環境的執行緒處理之間的同步,可能這讓人有些難以理解,但我們想想一下當你開闢了多條執行緒用於處理某種事物的時候,例如你訪問 Redis 中的 Set 介面,大多數的客戶端都需要阻塞當前執行緒,由另一個執行緒發射訊號通知核心停止阻塞(
18.執行緒同步:條件變數cond
1.條件變數 1.條件變數(Condtion Variable)是在多執行緒程式中用來實現“等待->喚醒”邏輯常用的方法。 2.條件變數+互斥鎖(一起使用) 執行緒同步——互斥鎖 阻塞執行緒——條件變數 2.條件變數使用場景舉例 舉個簡單的例子,應用程式A中包含兩
17.執行緒同步:讀寫鎖rwlock
1.讀寫鎖 讀鎖:共享 寫鎖:獨佔 1.多個執行緒可以對同一個共享資源加[讀鎖] 2.但是隻要有一個[寫鎖]到來,後面申請的鎖全部會阻塞 3.寫鎖優先:防止[寫操作]一直等待下去 當已經加了讀鎖,此時再來了寫鎖請求,之後又來了讀鎖請求====>後面到來的讀鎖請求不能 請求成功,只
16.執行緒同步:互斥鎖mutex
1.案例:兩個執行緒分別對一個變數,都++操作1000次 1.實際的執行結果:number的最終值<2000 2.資料混亂的原因: 多個執行緒操作了共享資源 CPU排程問題 3.解決:執行緒同步 int number=0;
19.執行緒同步:訊號量—>[單生產者/單消費者]單鏈表的插入和刪除
1.訊號量 1.訊號量本質 訊號量是鎖,是一種升級的mutex 訊號量在初始化時,可以指定共享資源的數量 2.相關函式 #include<semaphore.h> //標頭檔案 sem_t sem; //訊號量型別 int sem_destroy(se
18.執行緒同步:條件變數cond—>生產者消費者模型
條件變數:[單生產者/單消費者]單鏈表的插入和刪除 typedef struct node{//宣告連結串列型別 int data;
執行緒同步:互斥鎖,訊號量
同步概念: 當多個執行緒共享相同的一塊記憶體時(實際上在一個程序的各個執行緒之間,除了棧區的資料之外,其他的資料這幾個縣城之間都是相互共享的),需要確保每個執行緒看到一致的資料檢視。也就是說,這些執行緒在對資料進行操作時,應該是同步的,也就是說當一個執行緒正在操作一個數據時,其他執行緒無法同時對
Java多執行緒11:多執行緒同步操作
什麼情況下需要同步 1、當多執行緒併發,有多段程式碼同時執行時,有時希望某一段程式碼執行的過程中CPU不要切換到其他執行緒工作。這時就需要執行緒同步。 2、如果兩段程式碼是同步的,那麼同一段時間只能執行
C++——多執行緒程式設計(二)std::mutex 執行緒同步、解決資源競爭問題
前言 執行緒同步 這裡的“同”不是同時、一起執行的意思,而是指協同、協助、互相配合。執行緒同步是指多個執行緒協同步調,按預定的先後次序進行執行。 執行緒A和B一塊配合,A執行到一定程度時要依靠B的某個結果,於是停下來,示意B執行;B依言執行,再將結果給A;
Java執行緒同步:synchronized鎖住的是程式碼還是物件
在Java中,synchronized關鍵字是用來控制執行緒同步的,就是在多執行緒的環境下,控制synchronized程式碼段不被多個執行緒同時執行。synchronized既可以加在一段程式碼上,也可以加在方法上。 關鍵是,不要認為給方法或者程式碼段加上synchron
執行緒同步:Condition
使用Synchronized關鍵字(synchronized方法或者synchronized程式碼塊)來保證執行緒間同步,則系統中存在同步監視器(顯式或者隱式),可以使用wait()、notify()、notifyAll()來協調執行緒通訊。 但是當我們直接使用Lock物件來保證同步的話,則
執行緒同步:銀行帳戶存、取款問題
1.銀行賬戶類 package com.bankAccount.test; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.ut
1執行緒同步:互斥量,死鎖
1執行緒為什麼要同步 A:共享資源,多個執行緒都可對共享資源操作。 B:執行緒操作共享資源的先後順序不確定。 C:處理器對儲存器的操作一般不是原子操作。 2互斥量 mutex操作原語
作業系統(6)程序---程序概念:程序控制、程序狀態、三狀態模型、掛起模型;執行緒概念:使用者執行緒、核心執行緒、輕權執行緒
文章目錄 1:程序相關概念 1. 程序的概念 2. 程序控制塊 3. 程序狀態 4. 三狀態程序模型(就緒、執行、阻塞/等待) 5. 掛起程序模型 2:程序控制
Java開發之執行緒同步造成的執行緒死鎖
案例解析: 兩個人面對面過獨木橋,甲和乙都已經在橋上走了一段距離,即佔用了橋的資源,甲如果想通過獨木橋的話,乙必須退出橋面讓出橋的資源,讓甲通過,但是乙不服,為什麼讓我先退出去,我還想先過去呢,於是就僵持不下,導致誰也過不了橋,這就是死鎖。 死鎖產生情況解析: 1.互斥條件(
多執行緒(一):建立執行緒的幾種方法
概括來說就是兩種:1、繼承Thread類,重寫run方法,然後start。不推薦這種,因為java的單繼承特性。 2、Thread類的建構函式中可以接受Runnable任務,所以只要是Runnable例項就可以作為引數給Thread 一般有兩種建立Runnable例項的方法(1)實現Runn
執行緒三:多執行緒的問題
1、競態條件 當計算結果的正確性取決於相對時間或者排程器控制的多執行緒交叉時,就會發生競態條件。這句話可能對初次接觸執行緒的讀者來說不太好理解,其實競態條件有兩個相對比較好理解的描述,一個是check-then-act,另外一個是read-modify-wr
Java多執行緒12:多執行緒的死鎖
Java執行緒死鎖是一個經典的多執行緒問題,因為不同的執行緒都在等待根本不可能被釋放的鎖,從而導致所有的任務都無法繼續完成。在多執行緒技術中,“死鎖”是必須避免的,因為這會造成執行緒的“假死”。 pac