分散式系統詳解--基礎知識(併發)
分散式系統詳解--基礎知識(併發)
在前面曾經寫了一篇文章 時 分散式系統詳解--基礎知識(執行緒) 已經簡單介紹了一下關於執行緒的一些小故事,今天再來看一下我們的併發的問題,併發的情況出現。今天來了解一下併發的基本知識,然後通過內部程式上和應用程式上來看一下解決方案。
一、定義
併發,其實就是通過設計可以同時處理
二、併發能力
一個系統的併發能力大概有這麼幾個引數來決定的。
(1)響應時間:系統對請求作出的響應時間(請求 客戶端-->伺服器-->客戶端)
(2)吞吐量:單位時間內處理的請求數量
(3)QPS:每秒可以處理的請求數
(4)併發使用者數:同時承載正常使用系統功能的使用者數量
三、解決高併發
3.1 提升程式,上個鎖-synchronized
高併發量也就意味著多個執行緒同時在程式中亂竄,這樣就會導致資料的不安全性,體現在兩個方面,一是存在共享資料,另外一個就是共享資料可能被多個執行緒同時操作。而如果要保證多個執行緒同時訪問一個資源的時候,那麼就要給上限制,就是當一個執行緒訪問完一個資源之後,其他的執行緒才能進來訪問。這就需要一把鎖--互斥鎖!java中就提供了這麼一個關鍵字,就是synchronized,可以保證執行緒的訪問唯一性。
(1)先看看synchronizsd他的應用場景。
分類 | 具體方法 | 被鎖的物件 | 程式碼 |
方法 | 例項方法 | 類的例項物件 |
|
靜態方法 | 類物件 | |
|
程式碼塊 | 例項物件 | 類的例項物件 | |
Class物件 | 類物件 | |
|
任意例項物件Object | 例項物件Object | |
寫幾個簡單的程式碼例項。
A。作用在方法上
/**
*
*/
package com.yuyi;
/**
* @author mcb
*
* 2018年9月13日 下午3:56:57
*/
public class MyTest001 implements Runnable{
static int i=0;
public synchronized void method(){
i++;
}
@Override
public void run() {
// TODO 自動生成的方法存根
for (int i = 0; i<999; i++) {
this.method();
System.out.println("我是 "+Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) throws InterruptedException {
MyTest001 mytest001=new MyTest001();
Thread thread1=new Thread(mytest001,"1");
Thread thread2=new Thread(mytest001,"2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("ooooooooooooooooooooooooooooo"+i);
}
}
在這裡的程式碼結果可以通過下面的結果來看,當然這裡有一個Thread.join()的方法需要來進行說一下,這個方法其實就是當程序豎直向下進行的時候,如果沒有join(),這個方法的進行就是thread1,thread2和主執行緒三個程序在執行,而加上join()後,會返回該執行緒將該執行緒執行完畢之後再重新向下執行。我們可以通過下面的這個結果看到結果時1998,這就表明i的值是正確的。倘若沒有關鍵字synchronized,此時的結果不一定時1998。你可以把資料調的大一點試試~~
B。作用在靜態方法上
/**
*
*/
package com.yuyi;
/**
* @author mcb
*
* 2018年9月13日 下午3:56:57
*/
public class MyTest001 implements Runnable{
static int i=0;
public static synchronized void method(){
i++;
}
@Override
public void run() {
// TODO 自動生成的方法存根
for (int i = 0; i<99999; i++) {
method();
System.out.println("我是 "+Thread.currentThread().getName()+" "+i);
}
}
public static void main(String[] args) throws InterruptedException {
// MyTest001 mytest001=new MyTest001();
Thread thread1=new Thread(new MyTest001(),"1");
Thread thread2=new Thread(new MyTest001(),"2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("ooooooooooooooooooooooooooooo"+i);
}
}
在這段程式碼當中,值得注意的是,我們線上程的例項化上有一些改變,就是重新new了一個物件,並且在method()上也加了一個static,因為線上程上new了兩個物件,這個時候如果不是在method()上面加上靜態,就會產生一種情況,就是method的方法是各自物件的方法,此時此時同步鎖就失去了意義。當method()宣告為靜態時,此時就能保證該方法為全域性一致性。結果沒什麼問題。
C。作用在程式碼塊上
/**
*
*/
package com.yuyi;
/**
* @author mcb
*
* 2018年9月13日 下午3:56:57
*/
public class MyTest001 implements Runnable{
static MyTest001 mytest001=new MyTest001();
static int i=0;
public static synchronized void method(){
i++;
}
@Override
public void run() {
// TODO 自動生成的方法存根
synchronized(mytest001){
for (int i = 0; i<99999; i++) {
method();
System.out.println("我是 "+Thread.currentThread().getName()+" "+i);
}
}
}
public static void main(String[] args) throws InterruptedException {
Thread thread1=new Thread(mytest001,"1");
Thread thread2=new Thread(mytest001,"2");
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("ooooooooooooooooooooooooooooo"+i);
}
}
那什麼時候用程式碼塊呢?就是我們突然發現同步方法中需要進同步操作的程式碼就只有那麼一點點,但是其他的程式碼不作為同步操作卻存在於同步方法中,這個時候就可以將同步程式碼提取出來,放在程式碼塊中。可以自行檢視該程式碼的效果這裡就不做演示。
3.2 提升系統-分散式
(1)垂直擴充套件--不斷的提高一臺主機的效能
但是這種擴充套件擁有侷限性,因為如果該專案是公司內部用如erp系統,他們的使用者量和使用量也是很有侷限性,可以這樣進行操作,但是一但是面向社會,面向多使用者,一旦資料太大,就現在的伺服器而言,肯定是行不通的。
(2)橫向擴充套件--新增伺服器(nginx反向代理)
添加了伺服器的數量,也就所謂的伺服器分散式叢集。
(3)庫表雜湊
在資料庫叢集方面,很多資料庫都有自己的解決方案,Oracle、Sybase、MySQL等都有很好的方案。我們在應用程式中安裝業務和應用或者功能模組將資料庫進行分離,不同的模組對應不同的資料庫或者表,再按照一定的策略對某個頁面或者 功能進行更小的資料庫雜湊。
(4)HTML靜態化
對於大量內容並且頻繁更新的網站,我們無法全部手動去挨個實現,資訊釋出系統可以實現最簡單的資訊錄入自動生成靜態頁面,還能具備頻道管理、許可權 管理、自動抓取等功能,對於一個大型網站來說,擁有一套高效、可管理的CMS是必不可少的。
(5)靜態資源伺服器分離或者僅圖片伺服器分離
基本上大型網站都會採用的策略,他們都有獨立的圖片伺服器,甚至很多臺圖片伺服器。這樣的架構可以降低提供頁面訪問請求的伺服器系統壓力,並且可以保證系統不會因為圖片問題而崩潰,在應用伺服器和圖片伺服器上,可以進行不同的配置優化
當然也還有一些其他的方式,比如說快取、容器等。
參考部落格: 分散式、高併發、多執行緒Multithreading
歡迎訂閱關注公眾號(JAVA和人工智慧)
獲取更多免費書籍、資源、視訊資料
文章回顧連結: