并發與多線程

并發

  并發(concurrency)是指CPU在某個時間段內交替處理多任務的能力。每個CPU不可能只顧著執行某個進程,而讓其他進程一直等待被執行。所以,CPU把可執行時間均分成若干份,每個進程執行一份或多份時間后,記錄當前的工作狀態,釋放相關資源并進入等待狀態,讓其他進程搶占CPU等資源。

  在并發環境下,由于程序的封閉性被打破,出現了以下特點:

  1.并發程序之間有相互制約的關系。直接制約體現在一個程序需要另一個程序的計算結果;間接制約體現在多個進程競爭共享資源。

  2.并發程序的執行過程是斷斷續續的。程序需要保留現場,記憶現場指令及執行點。

  3.當并發數設置合理并且CPU擁有足夠的處理能力時,并發會提高程序的運行效率。

  在Java編程中,并發主要與線程有關。

線程

  線程是CPU調度和分派的基本單位,為了更充分地利用CPU資源,一般都會使用多線程進行處理。多線程的作用是提高任務的平均執行速度,但是會導致程序可解性變差,編程難度加大。所以,合適的線程數才能讓CPU資源被充分利用。

  每一個線程都有自己的操作棧、程序計數器、局部變量表等資源。同一進程內的所有線程都可以共享該進程的所有資源。

  Java提供了兩種形式定義線程類:

  1.實現Runnable接口并重寫其中的run()方法。

 1 class Consumer implements Runnable {
 2 
 3     private Store store;
 4 
 5     public Consumer(Store store) {
 6         this.store = store;
 7     }
 8 
 9     @Override
10     public void run() {
11         for (int i = 0; i < 1000; i++) {
12             store.getValue();
13         }
14     }
15 
16 }
Consumer

  2.繼承Thread類并重寫其中的run()方法。

 1 class Producer extends Thread {
 2 
 3     private Store store;
 4 
 5     public Producer(Store store) {
 6         this.store = store;
 7     }
 8 
 9     @Override
10     public void run() {
11         for (int i = 0; i < 1000; i++) {
12             store.setValue((int) (Math.random() * 100));
13         }
14     }
15 
16 }
Producer

  里氏代換原則對繼承的一個約束是子類不重寫父類的非抽象方法,而Thread類的run()方法不是一個抽象方法,所以繼承Thread類并重寫其中的run()方法就不符合里氏代換原則,該方式不推薦使用。相比之下,實現Runnable接口可以使編程更加靈活,對外暴露的細節也比較少,讓使用者專注于實現線程的run()方法。

線程狀態

  線程的生命周期分為以下5種狀態:

新建狀態

  新建狀態是線程被創建且未啟動的狀態。也就是說,初始化一個線程對象時,該對象進入新建狀態。

  線程對象的初始化分為2種:

  1.如果是繼承Thread類的線程類,則該類線程對象可以直接通過new運算進行初始化。

  2.如果是實現Runnable接口的線程類,則該類線程對象通過new運算進行初始化后需要包裝為一個Thread對象。

就緒狀態

  就緒狀態是線程啟動后運行之前的狀態。即啟動了的線程在準備執行run()方法時的狀態。

  線程的啟動是指線程對象調用Thread的start()方法。

運行狀態

  運行狀態是線程運行時的狀態,即啟動了的線程在執行run()方法時的狀態。

阻塞狀態

  阻塞狀態分以下3種情況:

  同步阻塞:缺少資源無法繼續運行。搶占到資源后會退出該狀態。

  主動阻塞:主動讓出CPU執行權,即線程執行Thread的sleep()方法之后的狀態。調用sleep()方法時會傳入一個long類型的參數,表示睡眠的時間,單位為毫秒,時間結束時會退出該狀態。

  等待阻塞:進入睡眠,即線程執行Object的wait()方法之后的狀態。其他線程執行Object的notify()方法或notifyAll()方法之后會退出該狀態。

終止狀態

  終止狀態是線程執行結束或因異常退出后的狀態。

線程同步

  線程同步機制的主要任務是,對多個相關線程在執行次序上進行協調,使并發執行的每個線程之間能按照一定的時序共享資源,并能很好地相互合作,從而使程序的執行具有可再現性。

  資源的共享分為兩種方式:

  互斥共享方式:某些資源例如打印機、磁帶機等,一次只能給一個線程使用,當一個線程申請該資源時,如果該資源有其他線程在使用,則該線程需要等待,直到資源被釋放之后才能申請。

  同時訪問方式:某些資源例如磁盤設備等,一次可以給多個線程“同時”訪問,這種“同時”是宏觀上的,實際上還是多個線程交替訪問。

  臨界資源指的是一段時間內只能由一個線程訪問的資源,而臨界區指的是每個線程中訪問臨界資源的那部分代碼。顯然,若能保證每個線程互斥地進入自己的臨界區,便可以實現每個線程對臨界資源的互斥訪問。為此,需要在每個線程進入臨界區前需要對訪問的臨界資源進行檢查,如果它是空閑的,則進入臨界區;否則等待,直到臨界資源空閑。具體流程如下:

  進入區:檢查臨界資源的狀態,如果空閑,則將其狀態改為被訪問,并進入臨界區;如果被訪問,則循環等待,直到其狀態變為空閑。

  臨界區:訪問臨界資源。

  退出區:將臨界資源的狀態改為空閑,并釋放臨界資源。

  Java提供synchronized關鍵字標識方法或代碼塊,被標識的方法稱為同步方法,被標識的代碼塊稱為同步代碼塊。每個對象都有一個監視器與之關聯。當線程通過該對象執行同步方法或同步代碼塊時,它首先試圖獲取監視器,如果獲取到監視器,則鎖定該對象,防止其他線程通過該對象執行同步方法或同步代碼塊,執行結束后,解鎖該對象并釋放監視器;如果獲取不到監視器,表示有其他線程通過該對象執行同步方法或同步代碼塊,則會進入等待。所以,監視器的作用就相當于進入區和退出區的作用。

  例如:定義兩種線程——生產者(Producer)和消費者(Consumer),生產者每次會產生一個數,消費者每次會取出一個數。Producer和Consumer線程對象通過同一個Store對象來調用Store的同步方法。

 1 class Store {
 2 
 3     private int value;
 4 
 5     public synchronized int getValue() {
 6         System.out.println("-取出" + value);
 7         return value;
 8     }
 9 
10     public synchronized void setValue(int value) {
11         this.value = value;
12         System.out.println("放入" + value);
13     }
14 
15 }
Store
1 @Test
2 void test() {
3     Store store = new Store();
4     Thread producer = new Producer(store);   // 繼承Thread類的線程類對象的初始化
5     Thread consumer = new Thread(new Consumer(store));   // 實現Runnable接口的線程類對象的初始化
6     producer.start();
7     consumer.start();
8 }
test

  部分輸出結果:

  

  當Consumer線程對象調用getValue()方法時,會獲取監視器,鎖定Store對象,直到方法返回后解鎖Store對象,釋放監視器;當Producer線程對象調用setValue()方法時也是如此。所以在創建Producer和Consumer線程對象時需要傳入同一個Store對象。如果傳入不同的Store對象,每一個Store對象都有一個監視器,則起不到鎖定的效果。

  根據輸出結果可以發現:取出多次數后才放入一次數,放入多次數后才取出一次數。要實現放入一個數后取出一個數的效果,則需要添加一個標識量。

  改進:在Store類中添加一個mutex標識量,當mutex為true時,表示Store內存了一個數,等待Consumer來取;為false時,表示Store內沒有數,等待Producer生產數。

 1 class Store {
 2 
 3     private int value;
 4     private boolean mutex;   // mutex初始值為false,表示沒有數
 5 
 6     public synchronized int getValue() {
 7         while (! mutex) {   // mutex為false時進入等待
 8             try {
 9                 wait();
10             } catch (InterruptedException e) {
11                 e.printStackTrace();
12             }
13         }
14         System.out.println("-取出" + value);
15         mutex = false;   // 取出數后將mutex置為false
16         notify();
17         return value;
18     }
19 
20     public synchronized void setValue(int value) {
21         while (mutex) {   // mutex為true時進入等待
22             try {
23                 wait();
24             } catch (InterruptedException e) {
25                 e.printStackTrace();
26             }
27         }
28         this.value = value;
29         System.out.println("放入" + value);
30         mutex = true;   // 放入數后將mutex置為true
31         notify();
32     }
33 
34 }
Store

  部分輸出結果:

  

轉載于:https://www.cnblogs.com/lqkStudy/p/11135153.html

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/247638.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/247638.shtml
英文地址,請注明出處:http://en.pswp.cn/news/247638.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

有沒有朋友可以幫我解釋一下貼水是什么意思?

通俗易懂的講&#xff1a;貼水便宜&#xff0c;升水貴 當前&#xff0c;螺紋鋼05合約就是貼水01合約 翻譯&#xff0c;螺紋鋼05合約就是比01合約便宜 升水同理 轉載于:https://www.cnblogs.com/luoluo-123/p/11142229.html

es6常用點記錄

letconst解構賦值字符串數組函數對象SymbolSetWeakSetMapWeakMapProxyreflectProxy與Reflex結合實例classpromiseiteratorGerneratorDecorators模塊學習資料 let /* let 聲明變量 *//* es6相對于es5的全局和局部作用域&#xff0c;多了一個塊作用域&#xff0c;塊作用域里聲明的…

jquery插件封裝指南

入門 編寫一個jQuery插件開始于給jQuery.fn加入??新的功能屬性&#xff0c;此處添加的對象屬性的名稱就是你插件的名稱&#xff1a; jQuery.fn.myPlugin function(){//你自己的插件代碼};用戶非常喜歡的$符號哪里去了&#xff1f; 它仍然存在&#xff0c;但是&#xff0c;為…

synchronize原理

synchronized的三種應用方式 一. 修飾實例方法&#xff0c;作用于當前實例加鎖&#xff0c;進入同步代碼前要獲得當前實例的鎖。 二. 修飾靜態方法&#xff0c;作用于當前類對象加鎖&#xff0c;進入同步代碼前要獲得當前類對象的鎖。 三. 修飾代碼塊&#xff0c;指定加鎖對象&…

不能修改“System Roots”鑰匙串

iOS mac添加證書 不能修改“System Roots”鑰匙串錯誤 如圖&#xff1a; 解決方式&#xff1a; 打開鑰匙串---登錄---&#xff0c;直接把證書拖過來 然后&#xff0c;查看--我的證書&#xff0c;里面&#xff0c;找到證書&#xff0c;即可

stylus在vue中的使用

stylus是一個css預處理器&#xff0c;比較流行的css預處理器有sass、less、stylus&#xff0c;它們都一樣&#xff0c;都是css的語法糖&#xff0c;可以使用變量&#xff0c;可以有簡單的邏輯&#xff0c;使css的開發效率更高&#xff0c;更易維護。stylus來自node社區&#xf…

未來產品的設計

Donald A. Norman繼《情感化設計》之后&#xff0c;又一設計精品力作&#xff1a; 未來產品的設計樣章試讀及本書預定&#xff1a;http://www.china-pub.com/195642市場價 &#xff1a;&#xffe5;39.00 會員價 &#xff1a; &#xffe5;29.25(75折) 【作  者】(美)Donald…

vue-cli webpack 配置分析

目錄結構 . ├── README.md ├── build │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.c…

css之字體圖標

SVG與字體圖標 SVG圖片是矢量圖片&#xff0c;不會隨著圖片的伸縮而影響質量&#xff0c;通常把只有一種顏色的圖標做成SVG&#xff0c;通過SVG生成字體圖標&#xff0c;放到項目中使用。 https://icomoon.io/是一個比較快捷的生成字體圖標的線上工具&#xff0c;進入主頁后&…

history.back(-1)和history.go(-1)的區別

history.back(-1):直接返回當前頁的上一頁&#xff0c;數據全部消息&#xff0c;是個新頁面 history.go(-1):也是返回當前頁的上一頁&#xff0c;不過表單里的數據全部還在 history.back(0) 刷新 history.back(1) 前進 history.back(-1) 后退

研發階段模擬接口數據

因為在vue-cli工程中需要創建很多.vue文件&#xff0c;我們希望創建vue文件和創建html、css、js文件一樣右鍵即可選擇創建&#xff0c;并且創建的文件中可預先寫好模板代碼。 webstorm-Preferences打開選項界面 選擇File and Code Templates&#xff0c;點擊綠色加號 填…

真實項目中 ThreadLocal 的妙用

一、什么是 ThreadLocal ThreadLocal 提供了線程的局部變量&#xff0c;每個線程都可以通過 set() 和 get() 來對這個局部變量進行操作&#xff0c;但不會和其他線程的局部變量沖突&#xff0c;實現了線程間的據隔離。 簡單講&#xff1a;一個獲取用戶的請求線程 A&#xff0c;…

css之flex布局

flex布局是css3中的重要布局方式&#xff0c;稱為“彈性布局”&#xff0c;每次想到它主要是遇到元素垂直居中、元素寬高自適應的問題&#xff0c;這些問題在flex中都能過簡單設置就解決&#xff0c;它更像是原生APP中的布局操作&#xff0c;布局不必寫N多的盒模型代碼來實現&a…

javascript對URL中的參數進行簡單加密處理

javascript的api本來就支持Base64&#xff0c;因此我們可以很方便的來進行編碼和解碼。 var encodeData window.btoa("namexiaoming&age10")//編碼 var decodeData window.atob(encodeData)//解碼。 下面來個具體的例子來說明如何對url中參數進行轉碼&#xff…

Fibinary Numbers

http://acm.hust.edu.cn/vjudge/contest/view.action?cid30506#problem/V 題意&#xff1a;從右向左&#xff0c;每一個位數&#xff0c;分別表示一個fibonacci數&#xff0c;1表示有&#xff0c;0表示沒有&#xff1b;求兩個數的和&#xff0c;同樣按照這種形式存儲 #include…

移動web開發DRP問題

DPR dpr問題是移動端web開發上需要注意的問題&#xff0c;用大白話說就是&#xff0c;代碼中所設置的像素值實際上是虛擬像素&#xff0c;手機屏幕上的像素實際為物理像素&#xff0c;原始的手機&#xff0c;虛擬像素與物理像素是1:1覆蓋的&#xff0c;但隨著移動端屏幕的技術發…

HTML元素title里面如何換行

在調試代碼的時候我就遇到一個問題&#xff0c;HTML元素title里面通常只顯示一行&#xff0c;那我想要他換行&#xff0c;就是多行顯示&#xff0c;如何實現&#xff1f;JS代碼里面比如Alert里面又該如何換行&#xff1f; 經過我的一番實驗 要實現這種效果有幾種方法&#xff0…

A20 GPIO中斷類型差別結果迥異的問題思考

A20GPIO中斷類型差別結果迥異的問題思考 最近在使用全志A20做開發時&#xff0c;發現在處理中斷的時候&#xff0c;用電平觸發模式&#xff0c;報中斷比較亂&#xff0c;用邊沿觸發則很穩定&#xff0c;不會亂報。筆者感到比較困惑&#xff0c;筆者用電平觸發寫的code如下&…

div內圖片和文字水平垂直居中

大小不固定的圖片、多行文字的水平垂直居中 本文綜述 想必寫css的都知道如何讓單行文字在高度固定的容器內垂直居中&#xff0c;但是您知道或者想過讓行數不固定的文字在高度固定的容器內垂直居中呢&#xff1f;本文將會告訴你如何實現多行文字的垂直居中顯示。 關于圖片垂直居…

sticky-footer實現記錄

sticky-footer是css中的一個經典問題&#xff1a; 當頁面內容超出屏幕&#xff0c;頁腳模塊會像正常頁面一樣&#xff0c;被推到內容下方&#xff0c;需要拖動滾動條才能看到。 而當頁面內容小于屏幕高度&#xff0c;頁腳模塊會固定在屏幕底部&#xff0c;就像是底邊距為零的…