Viterbi(維特比)算法在CRF(條件隨機場)中是如何起作用的?

之前我們介紹過BERT+CRF來進行命名實體識別,並對其中的BERT和CRF的概念和作用做了相關的介紹,然對於CRF中的最優的標籤序列的計算原理,我們只提到了維特比算法,並沒有做進一步的解釋,本文將對維特比算法做一個通俗的講解,以便大家更好的理解CRF為什麼能夠得到最優的標籤序列。

通過閱讀本文你將能回答如下問題:

  • 什麼是維特比算法?
  • 為什麼說維特比算法是一種動態規劃算法?
  • 維特比算法具體怎麼實現?

首先,讓我們簡單回顧一下BERT和CRF在命名實體識別中各自的作用:
命名實體識別中,BERT負責學習輸入句子中每個字和符號到對應的實體標籤的規律,而CRF負責學習相鄰實體標籤之間的轉移規則。詳情可以參考這篇文章。該文章中我們對CRF做了簡單易懂的介紹,其中提到CRF的損失函數計算要用到最優路徑,因為CRF的損失函數是求最優路徑的概率占所有路徑概率和的比例,而我們的目標是最大化這個比例。那麼這裏就涉及到計算最優路徑的問題。這裏的路徑在命名實體識別的例子中,就是最終輸出的與句子中的字或符號一 一對應的標籤序列。不同標籤序列的順序組成了不同的路徑。而CRF就是要找出最正確的那條標籤序列路徑,也就是說這條標籤路徑的概率將是所有路徑中最大的,那麼我們可以窮舉出所有可能的標籤路徑,計算出每條路徑的概率和,然後比較出最大的那條,但是這樣做的代價太大了,所以crf選擇了一種稱為維特比的算法來求解此類問題。

維特比算法(英語:Viterbi algorithm)是一種動態規劃算法。它用於尋找最有可能產生觀測事件序列的維特比路徑。

看看下面這個命名實體識別的例子:

上圖共有5層(觀測序列的長度),每層3個節點(狀態的個數),我們的目標就是找到從第一層到第五層的最優路徑。
首先,我們分別計算紅、黃、藍三個節點的輸入連線的概率,以紅色節點舉例,我們先假設紅色節點在最優路徑上,那麼輸入到該節點的三條連線中,概率最大的那條一定在最優路徑上,同理,我們再分別假設黃色和藍色節點在最優路徑上,我們也能各找到一條概率最大的連線,這樣就得到了下面的圖:

然後,我們接着剛才的思路繼續找後面一層的三條最優的連線:

假設找到的最優連線如下:

然後,接着在後面的層應用這個方法:

此時,看上面最後一張圖,我們有了3條候選最優路徑,分別是棕色、綠色和紫色,用標籤來表達如下:

那麼哪條才是最優路徑呢?
就是看哪條路徑的概率和最大,那條路徑就是最優路徑。
但是在實際實現的時候,一般會在計算各層的最優候選連線的時候,就記錄下前繼連線的概率和,並記錄下對應的狀態節點索引(這裏將已經計算出的結果記錄下來供後續使用的方式,就是維特比算法被稱為動態規劃算法的原因),這樣到最後一層的時候,最後一層各候選連線中概率最大的,就是在最優路徑上的那條連線了,然後從這條連線回溯,找出完整的路徑就是最優路徑了。

一直在說概率最大的路徑,那麼這個概率具體指什麼呢?
還記得上一篇文章介紹條件隨機場(CRF)的時候提到,條件隨機場其實是給定了觀測序列的馬爾可夫隨機場,在一階馬爾可夫模型中,定義了以下三個概念:

  • 狀態集合Q,對應到上面的例子就是:
    {B-P, I-P, O}
  • 初始狀態概率向量Π,對應到上面的例子就是:
    {B-P:0.3, I-P:0.2, O:0.5}
    這裏的概率數值是隨便假設的,僅為了方便舉例說明。
  • 狀態轉移概率矩陣A:

CRF中給定了觀測序列做為先驗條件,對應到上面的例子就是:

其中的概率數值同樣是隨便假設的,為了方便舉例。

下圖中紅色節點的概率(可以看成是一個虛擬的開始節點到該節點的連線的概率)的計算方式如下:
初始狀態為B-P的概率Π(B-P) * 該節點的觀測概率P(小|B-P)

下圖中紅色節點的三條連線概率的計算方式如下:
上一層對應節點的概率 * 上層對應節點到該節點的轉移概率 * 該節點的觀測概率P(明|B-P)

其它層之間的節點連線的概率同理計算可得,然後通過上面介紹的維特比算法過程就可以計算出最優路徑了。

ok,本篇就這麼多內容啦~,感謝閱讀O(∩_∩)O。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

收購3c瘋!各款手機、筆電、相機、平板,歡迎來詢價!

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

Alibaba Nacos 學習(五):K8S Nacos搭建,使用nfs

 

準備環境

Centos7  192.168.50.21 k8s-master 2G
Centos7  192.168.50.22 k8s-node01 2G
Centos7  192.168.50.23 k8s-node02 2G

K8S集群搭建參考 

 

master安裝好Git ,yum install git

master,node01,node02  安裝 nfs-utils

yum install nfs-utils

master,node01,node02添加nfs exports配置,為了解決後續的nfs報錯異常

/data/mysql-slave *(insecure,fsid=0,rw,async,no_root_squash)
/data/mysql-master *(insecure,fsid=0,rw,async,no_root_squash)
/data/nfs-share *(rw,fsid=0,sync,no_root_squash)
mysql-slave 數據庫從庫 
mysql-master 數據庫主庫
nfs-share nocas文件掛在目錄

後面的yml中會提到
master,node01,node02創建目錄
mkdir /data/mysql-slave
mkdir /data/mysql-master
mkdir /data/nfs-share 

 master 克隆代碼

   git clone https://github.com/nacos-group/nacos-k8s.git

克隆完成進入以下目錄

 cd /opt/nacos-k8s/deploy/

 

1.nfs安裝

kubectl create -f nfs/rbac.yaml 
kubectl create -f nfs/class.yaml 

修改nfs/deployment.yaml IP配置

 

 

 

kubectl create -f nfs/deployment.yaml

查看安裝狀態

kubectl get pod -l app=nfs-client-provisioner

 

 

 

2.mysql部署

cd /opt/nacos-k8s/deploy/mysql/

修改數據配置文件ip

vi mysql-master-nfs.yaml

 

 

 部署主庫

kubectl create -f mysql-master-nfs.yaml 

修改存庫ip

vi mysql-slave-nfs.yaml
kubectl create -f mysql-slave-nfs.yaml 

主從部署非常慢 耐心等待,如果報nfs相關的錯,重啟nfs即可

service nfs restart

 

 

3. 部署nacos

cd /opt/nacos-k8s/deploy/nacos/

 

 

 

 

 

kubectl create -f nacos-pvc-nfs.yaml 

 查看訪問端口

kubectl get svc|grep nacos

 

 

 

 

 查看K8S集群狀態

 

 Failed to pull image “nacos/nacos-server:latest”: rpc error: code = Unknown desc = context canceled

進去對應節點機器 ,拉取鏡像后,重新應用即可

kubectl apply -f

 4. 部署問題

部署過程中大部分都是NFS問題

可以參考

mount.nfs: No route to host
Warning FailedMount 100s (x5 over 10m) kubelet, node2 Unable to mount volumes for pod “nfs-client-provisioner-594f778474-whhb5_default(56aef93a-9d31-11e9-a4c4-00163e069f44)”: timeout expired waiting for volumes to attach or mount for pod “default”/”nfs-client-provisioner-594f778474-whhb5”. list of unmounted volumes=[nfs-client-root]. list of unattached volumes=[nfs-client-root nfs-client-provisioner-token-8dcrx]

修改deployment.yaml中server的IP地址為某個node節點的內網IP地址,圖1已標註

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※公開收購3c價格,不怕被賤賣!

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

Redis 4.0鮮為人知的功能將加速您的應用程序

來源:Redislabs

作者:Kyle Davis

翻譯:Kevin (公眾號:中間件小哥)

Redis 4.0給Redis生態帶來了一個驚人的功能:Modules(模塊)。Modules是Redis的一大轉變,它是Redis內部自定義數據類型和全速計算的開放環境。但是,儘管對該版本的大多數關注都集中在Modules上,但新版本還引入了一個非常重要的命令,它就是遊戲規則的改變者:UNLINK。

您可以使用redis-cli連接redis-server執行info命令,去查看當前redis版本中是否可以使用UNLINK命令。info響應將告訴您有關服務器的所有信息。在第一部分(#Server)中,返回結果有一行值為redis_version。如果該值大於4.0,則可以使用UNLINK命令。並非所有Redis提供商都保持最新版本,因此最好在更改代碼之前檢查redis版本。

讓我們回顧一下Redis的關鍵架構功能之一:“單線程”。Redis在大多數情況下是一個單線程應用程序。它一次只做一件事,這樣可以把這些事做的更快。多線程有點複雜,並且引入了鎖和其他可能降低應用程序速度的問題。儘管Redis(最高4.0版)通過多線程方式執行了少量操作,但它通常在啟動另一個命令之前先要完成一個命令。

相比於快速讀寫,您可能會覺得使用DEL命令去刪除一個鍵值不需要考慮太多,但是在很多情況下,刪除數據同樣很重要。與Redis中的大多數命令一樣,DEL命令在單個線程中運行,如果您獲取一個幾千字節的鍵值,花費不到一毫秒的時間,這是您所感知不到的。然而,當您獲取的鍵值大小是兆字節、100兆字節或者500兆字節會發生什麼呢?哈希、排序、列表等數據結構會隨着時間的推移而添加更多的數據進去,這樣會生成一個數GB大小的數據集。然後用DEL命令去刪除大Key時會發生什麼呢?由於Redis是單線程操作的,處理這種請求時整個服務都處於等待中,需要等待該命令執行完成才能執行其它操作。同時,我們考慮更複雜的一種場景,這些鍵中保存的數據可能已經包含數以千萬個微小請求,因此應用程序或操作員可能無法真正了解刪除這些數據需要花費多長時間。

理智會告訴我們不要在擁有100萬元素的排序集上運行如下這樣的命令:

> ZRANGE some-zset 0 -1

但是,在上面的some-zset集合中執行DEL命令將花費和上面一樣的時間-中間沒有傳輸開銷,但是它會一直去分配內存,而且您會一直卡死在CPU繁忙中。在使用UNLINK之前,您可能會結合SCAN命令採用非原子性的方法進行一些少量刪除,去避免這種持續分配內存的噩夢。上面無論使用哪種方式,都是讓人無法接受的。

您可能已經猜到了,就是使用UNLINK命令來替換DEL!從語法上講,UNLINK與DEL相同,但UNLINK提供了更為理想的解決方案。首先,它將鍵值從整個鍵值空間中刪除。然後,在另一個線程中,它開始回收內存。從多線程的角度來看,這是一種安全的操作,因為它(在主線程中)從鍵空間中刪除了該項,從而使Redis其它命令無法訪問。

如果你有一個快速增長的鍵值-不管鍵值的大小如何,UNLINK都是O(1)操作(每個鍵;在主線程中)。使用DEL刪除一個大值可能需要幾百毫秒或更長時間,而UNLINK將在不到一毫秒的時間內完成(包括網絡往返)。當然,您的服務器仍將需要花一些時間在另一個線程中重新分配該值的內存(其中的工作是O(N),其中N是已刪除值的分配數),但是主線程的性能不會被另一個線程中正在進行的操作嚴重影響到。

因此,您是否應該用UNLINK命令替換代碼中的所有DEL命令?當然,在少數情況下,DEL正是您所需要的。這裏我可以想到兩點:

1、   在MULTI / EXEC或pipeline中,在添加和刪除大值時DEL命令是一種理想選擇。在這種情況下,UNLINK不會立即釋放空間,並且在處理繁忙的情況下(如果內存已滿),您可能會遇到麻煩。

2、   在更緊急的情況下,在無快速響應驅逐數據下您可以寫入數據。

在沒有極端內存限制的理想環境中,很難想到不使用UNLINK的情況。UNLINK將提供更一致的行為,總體上具有更好的性能,並且代碼更改非常小(如果可以在客戶端中重命名命令,則無需更改)。如果UNLINK適合您的應用程序,請就此將您的DEL更改為UNLINK,然後查看它的性能提高。

 

更多優質中間件技術資訊/原創/翻譯文章/資料/乾貨,請關注“中間件小哥”公眾號!

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※高價收購3C產品,價格不怕你比較

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

3c收購,鏡頭 收購有可能以全新價回收嗎?

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

http協議詳細介紹

1.1 HTTP協議簡介
我們日常生活中經常會使用瀏覽器訪問Web站點,但是大家有思考過在這個過程中到底發生了什麼嗎?為什麼我們在瀏覽器地址欄上面輸入要訪問的URL后就可以訪問到Web頁面呢?

1.1.1 瀏覽器背後的故事
當我們在瀏覽器地址欄上輸入要訪問的URL后,瀏覽器會分析出URL上面的域名,然後通過DNS服務器查詢出域名映射的IP地址,瀏覽器根據查詢到的IP地址與Web服務器進行通信,而通信的協議就是HTTP協議。

我們可以把這個過程類比成一個電話對話的過程。當我們要打電話給某個人,首先要知道對方的電話號碼,然後進行撥號。打通電話后我們會進行對話,當然要對話肯定需要共同的語言,如果一個人說國語,而另一個人說英語,那肯定不能進行溝通的。在本例中,電話號碼相當於上面的IP地址,而共同語言相當於HTTP協議。

我們通過一個簡單的圖來闡述這個過程:

瀏覽器與Web服務器使用HTTP協議進行通信,那麼什麼是HTTP協議呢?接下來我們會詳細介紹HTTP協議的相關知識。

1.1.2 TCP/IP協議
HTTP協議是構建在TCP/IP協議之上的,是TCP/IP協議的一個子集,所以要理解HTTP協議,有必要先了解下TCP/IP協議相關的知識。

由於TCP/IP協議族包含眾多的協議,在這裏我們無法一一討論。接下來,我們僅介紹理解HTTP協議需要掌握的TCP/IP協議族的一些相關知識點。如果想深入理解TCP/IP協議,可以參考經典書籍《TCP/IP詳解》。

TCP/IP協議族分層

TCP/IP協議族是由一個四層協議組成的系統,這四層分別為:應用層、傳輸層、網絡層和數據鏈路層。如圖所示

分層的好處是把各個相對獨立的功能解耦,層與層之間通過規定好的接口來通信。如果以後需要修改或者重寫某一個層的實現,只要接口保持不變也不會影響到其他層的功能。接下來,我們將會介紹各個層的主要作用。

1) 應用層

應用層一般是我們編寫的應用程序,其決定了向用戶提供的應用服務。應用層可以通過系統調用與傳輸層進行通信。

處於應用層的協議非常多,比如:FTP(File Transfer Protocol,文件傳輸協議)、DNS(Domain Name System,域名系統)和我們本章討論的HTTP(HyperText Transfer Protocol,超文本傳輸協議)等。

2) 傳輸層

傳輸層通過系統調用嚮應用層提供處於網絡連接中的兩台計算機之間的數據傳輸功能。

在傳輸層有兩個性質不同的協議:TCP(Transmission Control Protocol,傳輸控制協議)和UDP(User Data Protocol,用戶數據報協議)。

3) 網絡層

網絡層用來處理在網絡上流動的數據包,數據包是網絡傳輸的最小數據單位。該層規定了通過怎樣的路徑(傳輸路線)到達對方計算機,並把數據包傳輸給對方。

4) 鏈路層

鏈路層用來處理連接網絡的硬件部分,包括控制操作系統、硬件設備驅動、NIC(Network Interface Card,網絡適配器)以及光纖等物理可見部分。硬件上的範疇均在鏈路層的作用範圍之內。

數據包封裝

上層協議數據是如何轉變為下層協議數據的呢?這是通過封裝(encapsulate)來實現的。應用程序數據在發送到物理網絡之前,會沿着協議棧從上往下傳遞。每層協議都將在上層協議數據的基礎上加上自己的頭部信息(鏈路層還會加上尾部信息),以為實現該層功能提供必要的信息。如圖所示:

發送端發送數據時,數據會從上層傳輸到下層,且每經過一層都會被打上該層的頭部信息。而接收端接收數據時,數據會從下層傳輸到上層,傳輸前會把下層的頭部信息刪除。過程如圖所示:

數據傳輸過程

由於下層協議的頭部信息對上層協議是沒有實際的用途,所以在下層協議傳輸數據給上層協議的時候會把該層的頭部信息去掉,這個封裝過程對於上層協議來說是完全透明的。這樣做的好處是,應用層只需要關心應用服務的實現,而不用管底層的實現。

TCP三次握手

從上面的介紹可知,傳輸層協議主要有兩個:TCP協議和UDP協議。TCP協議相對於UDP協議的特點是:TCP協議提供面向連接、字節流和可靠的傳輸。

使用TCP協議進行通信的雙方必須先建立連接,然後才能開始傳輸數據。TCP連接是全雙工的,也就是說雙方的數據讀寫可以通過一個連接進行。為了確保連接雙方可靠性,在雙方建立連接時,TCP協議採用了三次握手(Three-way handshaking)策略。
過程如圖所示:

TCP協議三次握手的描述如下:

第一次握手:客戶端發送帶有SYN標誌的連接請求報文段,然後進入SYN_SEND狀態,等待服務端的確認。

第二次握手:服務端接收到客戶端的SYN報文段后,需要發送ACK信息對這個SYN報文段進行確認。同時,還要發送自己的SYN請求信息。服務端會將上述的信息放到一個報文段(SYN+ACK報文段)中,一併發送給客戶端,此時服務端將會進入SYN_RECV狀態。

第三次握手:客戶端接收到服務端的SYN+ACK報文段后,會想服務端發送ACK確認報文段,這個報文段發送完畢后,客戶端和服務端都進入ESTABLISHED狀態,完成TCP三次握手。

當三次握手完成后,TCP協議會為連接雙方維持連接狀態。為了保證數據傳輸成功,接收端在接收到數據包后必須發送ACK報文作為確認。如果在指定的時間內(這個時間稱為重新發送超時時間),發送端沒有接收到接收端的ACK報文,那麼就會重發超時的數據。

1.1.3 DNS服務
前面介紹了與HTTP協議有着密切關係的TCP/IP協議,接下來介紹的DNS服務也是與HTTP協議有着密不可分的關係。

通常我們訪問一個網站,使用的是主機名或者域名來進行訪問的。因為相對於IP地址(一組純数字),域名更容易讓人記住。但TCP/IP協議使用的是IP地址進行訪問的,所以必須有個機制或服務把域名轉換成IP地址。DNS服務就是用來解決這個問題的,它提供域名到IP地址之間的解析服務。

如下圖所示,展示了DNS服務把域名解析成IP地址的過程:

DNS服務是通過DNS協議進行通信的,而DNS協議跟HTTP協議一樣也是應用層協議。由於我們的重點是HTTP協議,所以這裏不打算對DNS協議進行詳細的分析,我們只需要知道可以通過DNS服務把域名解析成IP地址即可。

1.1.4 HTTP與TCP/IP、DNS的關係
到現在,我們介紹了與HTTP協議有密切關係的TCP/IP協議和DNS服務,接下來我們通過下圖來整理一下HTTP協議與它們之間的關係:

HTTP與TCP/IP、DNS的關係
從上圖中可以知道,當客戶端訪問Web站點時,首先會通過DNS服務查詢到域名的IP地址。然後瀏覽器生成HTTP請求,並通過TCP/IP協議發送給Web服務器。Web服務器接收到請求後會根據請求生成響應內容,並通過TCP/IP協議返回給客戶端。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品在網路上成為最夯、最多人討論的話題?

※高價收購3C產品,價格不怕你比較

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

salesforce lightning零基礎學習(十五) 公用組件之 獲取表字段的Picklist(多語言),salesforce 零基礎學習(六十二)獲取sObject中類型為Picklist的field values(含record type)

此篇參考:

 我們在lightning中在前台會經常碰到獲取picklist的values然後使用select option進行渲染成下拉列表,此篇用於實現針對指定的sObject以及fieldName(Picklist類型)獲取此字段對應的所有可用的values的公用組件。因為不同的record type可能設置不同的picklist values,所以還有另外的參數設置針對指定的record type developer name去獲取指定的record type對應的Picklist values.

一. PicklistService公用組件聲明實現

Common_PicklistController.cls:有三個形參,其中objectName以及fieldName是必填參數,recordTypeDeveloperName為可選參數。

 1 public without sharing class Common_PicklistController {
 2     
 3     @AuraEnabled(cacheable=true)
 4     public static List<Map<String,String>> getPicklistValues(String objectName, String fieldName,String recordTypeDeveloperName) {
 5         //1. use object and field name get DescribeFieldResult and also get all the picklist values
 6         List<Schema.PicklistEntry> allPicklistValuesByField;
 7         try {
 8             List<Schema.DescribeSobjectResult> objDescriptions = Schema.describeSObjects(new List<String>{objectName});
 9             Schema.SObjectField field = objDescriptions[0].fields.getMap().get(fieldName);
10             Schema.DescribeFieldResult fieldDescribeResult = field.getDescribe();
11             allPicklistValuesByField = fieldDescribeResult.getPicklistValues();
12         } catch (Exception e) {
13             throw new AuraHandledException('Failed to retrieve values : '+ objectName +'.'+ fieldName +': '+ e.getMessage());
14         }
15         
16         //2. get all active field name -> label map
17         List<Map<String,String>> activeOptionMapList = new List<Map<String,String>>();
18         Map<String,String> optionValue2LabelMap = new Map<String,String>();
19         List<String> optionValueList;
20         for(Schema.PicklistEntry entry : allPicklistValuesByField) {
21             if (entry.isActive()) {
22                 System.debug(LoggingLevel.INFO, '*** entry: ' + JSON.serialize(entry));
23                 optionValue2LabelMap.put(entry.getValue(), entry.getLabel());
24             }
25         }
26 
27         //3. generate list with option value(with/without record type)
28         if(String.isNotBlank(recordTypeDeveloperName)) {
29             optionValueList = PicklistDescriber.describe(objectName,recordTypeDeveloperName,fieldName);
30         } else {
31             optionValueList = new List<String>(optionValue2LabelMap.keySet());
32         }
33 
34         //4. generate and format result
35         if(optionValueList != null) {
36             for(String option : optionValueList) {
37                 String optionLabel = optionValue2LabelMap.get(option);
38                 Map<String,String> optionDataMap = new Map<String,String>();
39                 optionDataMap.put('value',option);
40                 optionDataMap.put('label', optionLabel);
41                 activeOptionMapList.add(optionDataMap);
42             }
43         }
44 
45         return activeOptionMapList;
46     }
47 }

 Common_PicklistService.cmp:聲明了getPicklistInfo方法,有以下三個主要參數.objectName對應sObject的API名字,fieldName對應的此sObject中的Picklist類型的字段,recordTypeDeveloperName對應這個sObject的record type的developer name

1 <aura:component access="global" controller="Common_PicklistController">
2     <aura:method access="global" name="getPicklistInfo"  description="Retrieve active picklist values and labels mapping with(without) record type" action="{!c.getPicklistInfoAction}">
3         <aura:attribute type="String" name="objectName" required="true" description="Object name"/>
4         <aura:attribute type="String" name="fieldName" required="true" description="Field name"/>
5         <aura:attribute type="String" name="recordTypeDeveloperName" description="record type developer name"/>
6         <aura:attribute type="Function" name="callback" required="true" description="Callback function that returns the picklist values and labels mapping as [{value: String, label: String}]"/>
7     </aura:method>
8 </aura:component>

 Common_PicklistServiceController.js: 獲取傳遞過來的參數,調用後台方法並對結果放在callback中。

 1 ({
 2     getPicklistInfoAction : function(component, event, helper) {
 3         const params = event.getParam('arguments');
 4         const action = component.get('c.getPicklistValueList');
 5         action.setParams({
 6             objectName : params.objectName,
 7             fieldName : params.fieldName,
 8             recordTypeDeveloperName : params.recordTypeDeveloperName
 9         });
10         action.setCallback(this, function(response) {
11             const state = response.getState();
12             if (state === 'SUCCESS') {
13                 params.callback(response.getReturnValue());
14             } else if (state === 'ERROR') {
15                 console.error('failed to retrieve picklist values for '+ params.objectName +'.'+ params.fieldName);
16                 const errors = response.getError();
17                 if (errors) {
18                     console.error(JSON.stringify(errors));
19                 } else {
20                     console.error('Unknown error');
21                 }
22             }
23         });
24 
25         $A.enqueueAction(action);
26     }
27 })

二. 公用組件調用

 上面介紹了公用組件以後,下面的demo是如何調用。

SimplePicklistDemo引入Common_PicklistService,設置aura:id用於後期獲取到此component,從而調用方法

 1 <aura:component implements="flexipage:availableForAllPageTypes">
 2     <!-- include common picklist service component -->
 3     <c:Common_PicklistService aura:id="service"/>
 4     <aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
 5 
 6     <aura:attribute name="accountTypeList" type="List"/>
 7     <aura:attribute name="accountTypeListByRecordType" type="List"/>
 8 
 9     <lightning:layout verticalAlign="center" class="x-large">
10         <lightning:layoutItem flexibility="auto" padding="around-small">
11             <lightning:select label="account type">
12                 <aura:iteration items="{!v.accountTypeList}" var="type">
13                     <option value="{!type.value}" text="{!type.label}"></option>
14                 </aura:iteration>
15             </lightning:select>
16         </lightning:layoutItem>
17 
18         <lightning:layoutItem flexibility="auto" padding="around-small">
19             <lightning:select label="account type with record type">
20                 <aura:iteration items="{!v.accountTypeListByRecordType}" var="type">
21                     <option value="{!type.value}" text="{!type.label}"></option>
22                 </aura:iteration>
23             </lightning:select>
24         </lightning:layoutItem>
25     </lightning:layout>
26     
27 </aura:component>

SimplePicklistDemoController.js:初始化方法用於獲取到公用組件component然後獲取Account的type的values,第一個是獲取所有的values/labels,第二個是獲取指定record type的values/labels。

 1 ({
 2     doInit : function(component, event, helper) {
 3         const service = component.find('service');
 4         service.getPicklistInfo('Account','type','',function(result) {
 5             component.set('v.accountTypeList', result);
 6         });
 7 
 8         service.getPicklistInfo('Account','type','Business_Account',function(result) {
 9             component.set('v.accountTypeListByRecordType',result);
10         });
11     }
12 })

三.效果展示:

1. account type的展示方式

 

 2. account type with record type的展示方式。

 

 總結:篇中介紹了Picklist values針對with/without record type的公用組件的使用,感興趣的可以進行優化,篇中有錯誤的歡迎指出,有不懂的歡迎留言。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

收購3c瘋!各款手機、筆電、相機、平板,歡迎來詢價!

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選

Clean Code 筆記 之 第四章 如何應用註釋

繼上一篇筆記之後,今天我們討論一下 代碼中是存在註釋是否是一件好的事情。

 

在我們開發的過程中講究“名副其實,見名識意”,這也往往是很多公司的要求,但是有了這些要求是不是我們的代碼中如果存在註釋是不是意味着我們的 函數,變量,以及類 的命名就不符合了“名副其實,見名識意”。

我們先區分一下註釋的類別,註釋一般分為以下幾種:

  • 1, 單行註釋
  • 2, 多行註釋
  • 3, 文檔註釋
  • 4, #region 摺疊註釋,可以將 代碼摺疊

 註釋的類別

1, 單行註釋:

在以 “//” 開頭,用以說明一行代碼的作用放置位置 看習慣或者公司要求合理就行。常用於函數內部,在很多的開源代碼中文件的頭部我同樣見到很多人使用單行註釋進行說明,靈活就好。
體現形式如下:

 public List<string> getVipUserNameByUserType()
          {
            // Vip user name list
            var vipUserNames = new List<string>();

            foreach (var user in Users)
            {

                if (user.Type = "VIP")

                    vipUserNames.Add(user.Name);
            }
            return vipUserNames;

          }

View Code

2, 多行註釋:

以“/*”開頭 “*/” 結尾 常用於描述說明一段代碼以及類註釋或者說代碼塊常用於文件的頂部。說明作者信息,時間如果是開源的這包含開源的更多說明。
通常使用如下:

/*
    * 作者:〈版權〉
    * 描述:〈描述〉
    * 創建時間:YYYY-MM-DD
    * 作用:
*/

View Code

3, 文檔註釋:

也就是常用的XML 註釋:它的說明性更加的強烈,多用於類以及函數上,當然屬性上同樣可以使用:
如下所示:

        /// <summary>
        /// MyClass
        /// </summary>
        public class MyClass
        {
            /// <summary>
            /// MyProperty
            /// </summary>
            public int MyProperty { get; set; }
            /// <summary>
            /// MyMethod
            /// </summary>
            public void MyMethod(){  }
        }

View Code

以下是官方建議的文檔標記 點擊標籤會制動跳轉

 

4, #region : 摺疊註釋,常用於描述多個函數的基本作用

書中最喜歡的話

好的註釋不能美化糟糕的代碼,真正好的註釋是你想盡辦法不去謝的註釋。懷註釋都是糟糕代碼的支撐或借口,或者是對錯誤決策的修正。

下面看一個例子

       //Check to see if the employee is eligible for full benefits1)If((employee.flags & HOURLY_FLAG)&& (employee.age>65))

(2)If(employee.isEligibleForFullBenefits()))

  這兩個你更喜歡哪個

View Code

好的註釋的特徵:

1:表示法律信息(這樣的註釋一般出現在文檔頂部說明作用以及協議)

// Copyright (c) .NET Foundation. All rights reserved
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

View Code

2:提供信息的註釋(指無法通過命名提供信息如要 註釋輔助的)

public void ConfigureServices(IServiceCollection services)
{

// These two middleware are registered via an IStartupFilter in UseIISIntegration but you can configure them here.

services.Configure<IISOptions>(options =>
{});

}

View Code

3:對意圖的解釋

4: 警示:告知其他人會出現某種後果的註釋

5: TODO註釋: 主要描述應該做的目前還沒有做的事情。

6: 放大:提示不合理之物的重要性。

應避免的註釋

應該避免以下幾點:

1: 誤導性註釋

2: 日誌式註釋

3: 廢話註釋

4: 標記位置的註釋

5: 括號后的註釋

6: 歸屬與簽名

7: 註釋掉的代碼

8: Html 註釋

以上沒有一一舉例的原因是我的PPT是一份演示的PPT,裏面很多公司的代碼不便貼出,抱歉。

不足之處還請指出

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※公開收購3c價格,不怕被賤賣!

※想知道網站建置網站改版該如何進行嗎?將由專業工程師為您規劃客製化網頁設計後台網頁設計

※不管是台北網頁設計公司台中網頁設計公司,全省皆有專員為您服務

※Google地圖已可更新顯示潭子電動車充電站設置地點!!

※帶您來看台北網站建置台北網頁設計,各種案例分享

PHP 的 self 關鍵字用法

之前有人詢問 self 關鍵字的用法,答案是比較明顯的:靜態成員函數內不能用 this 調用非成員函數,但可以用 self 調用靜態成員函數/變量/常量;其他成員函數可以用 self 調用靜態成員函數以及非靜態成員函數。隨着討論的深入,發現 self 並沒有那麼簡單。鑒於此,本文先對幾個關鍵字做對比和區分,再總結 self 的用法。

parentstatic 以及 this 的區別

要想將徹底搞懂 self ,要與 parentstatic 以及 this 區分開。以下分別做對比。

parent

selfparent 的區分比較容易: parent 引用父類/基類被隱蓋的方法(或變量), self則引用自身方法(或變量)。例如構造函數中調用父類構造函數:

class Base {
    public function __construct() {
        echo "Base contructor!", PHP_EOL;
    }
}

class Child {
    public function __construct() {
        parent::__construct();
        echo "Child contructor!", PHP_EOL;
    }
}

new Child;
// 輸出:
// Base contructor!
// Child contructor!

 

static

static 常規用途是修飾函數或變量使其成為類函數和類變量,也可以修飾函數內變量延長其生命周期至整個應用程序的生命周期。但是其與 self 關聯上是PHP 5.3以來引入的新用途:靜態延遲綁定。

有了 static 的靜態延遲綁定功能,可以在運行時動態確定歸屬的類。例如:

class Base {
    public function __construct() {
        echo "Base constructor!", PHP_EOL;
    }

    public static function getSelf() {
        return new self();
    }

    public static function getInstance() {
        return new static();
    }

    public function selfFoo() {
        return self::foo();
    }

    public function staticFoo() {
        return static::foo();
    }

    public function thisFoo() {
        return $this->foo();
    }

    public function foo() {
        echo  "Base Foo!", PHP_EOL;
    }
}

class Child extends Base {
    public function __construct() {
        echo "Child constructor!", PHP_EOL;
    }

    public function foo() {
        echo "Child Foo!", PHP_EOL;
    }
}

$base = Child::getSelf();
$child = Child::getInstance();

$child->selfFoo();
$child->staticFoo();
$child->thisFoo();

 

程序輸出結果如下:

Base constructor!
Child constructor!
Base Foo!
Child Foo!
Child Foo!

 

在函數引用上, selfstatic 的區別是:對於靜態成員函數, self 指向代碼當前類, static 指向調用類;對於非靜態成員函數, self 抑制多態,指向當前類的成員函數, static 等同於 this ,動態指向調用類的函數。

parentselfstatic 三個關鍵字聯合在一起看挺有意思,分別指向父類、當前類、子類,有點“過去、現在、未來”的味道。

this

selfthis 是被討論最多,也是最容易引起誤用的組合。兩者的主要區別如下:

  1. this 不能用在靜態成員函數中, self 可以;
  2. 對靜態成員函數/變量的訪問, 建議 用 self ,不要用 $this::$this-> 的形式;
  3. 對非靜態成員變量的訪問,不能用 self ,只能用 this ;
  4. this 要在對象已經實例化的情況下使用, self 沒有此限制;
  5. 在非靜態成員函數內使用, self 抑制多態行為,引用當前類的函數;而 this 引用調用類的重寫(override)函數(如果有的話)。

self 的用途

看完與上述三個關鍵字的區別, self 的用途是不是呼之即出?一句話總結,那就是: self總是指向“當前類(及類實例)”。詳細說則是:

  1. 替代類名,引用當前類的靜態成員變量和靜態函數;
  2. 抑制多態行為,引用當前類的函數而非子類中覆蓋的實現;

槽點

  1. 這幾個關鍵字中,只有 this 要加 $ 符號且必須加,強迫症表示很難受;
  2. 靜態成員函數中不能通過 $this-> 調用非靜態成員函數,但是可以通過 self:: 調用,且在調用函數中未使用 $this-> 的情況下還能順暢運行。此行為貌似在不同PHP版本中表現不同,在當前的7.3中ok;
  3. 在靜態函數和非靜態函數中輸出 self ,猜猜結果是什麼?都是 string(4) "self" ,迷之輸出;
  4. return $this instanceof static::class; 會有語法錯誤,但是以下兩種寫法就正常:
    $class = static::class;
    return $this instanceof $class;
    // 或者這樣:
    return $this instanceof static;

 

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

網頁設計公司推薦更多不同的設計風格,搶佔消費者視覺第一線

※廣告預算用在刀口上,網站設計公司幫您達到更多曝光效益

※公開收購3c價格,不怕被賤賣!

※自行創業 缺乏曝光? 下一步"網站設計"幫您第一時間規劃公司的門面形象

高併發編程學習(1)——併發基礎

為更良好的閱讀體驗,請訪問原文:

一、前言

當我們使用計算機時,可以同時做許多事情,例如一邊打遊戲一邊聽音樂。這是因為操作系統支持併發任務,從而使得這些工作得以同時進行。

  • 那麼提出一個問題:如果我們要實現一個程序能一邊聽音樂一邊玩遊戲怎麼實現呢?
public class Tester {

    public static void main(String[] args) {
        System.out.println("開始....");
        playGame();
        playMusic();
        System.out.println("結束....");
    }

    private static void playGame() {
        for (int i = 0; i < 50; i++) {
            System.out.println("玩遊戲" + i);
        }
    }

    private static void playMusic() {
        for (int i = 0; i < 50; i++) {
            System.out.println("播放音樂" + i);
        }
    }
}

我們使用了循環來模擬過程,因為播放音樂和打遊戲都是連續的,但是結果卻不盡人意,因為函數體總是要執行完之後才能返回。那麼到底怎麼解決這個問題?

并行與併發

并行性和併發性是既相似又有區別的兩個概念。

并行性是指兩個或多個事件在同一時刻發生。而併發性是指兩個或多個事件在同一時間間隔內發生。

在多道程序環境下,併發性是指在一段時間內宏觀上有多個程序在同時運行,但在單處理機環境下(一個處理器),每一時刻卻僅能有一道程序執行,故微觀上這些程序只能是分時地交替執行。例如,在 1 秒鐘時間內,0 – 15 ms 程序 A 運行;15 – 30 ms 程序 B 運行;30 – 45 ms 程序 C 運行;45 – 60 ms 程序 D 運行,因此可以說,在 1 秒鐘時間間隔內,宏觀上有四道程序在同時運行,但微觀上,程序 A、B、C、D 是分時地交替執行的。

如果在計算機系統中有多個處理機,這些可以併發執行的程序就可以被分配到多個處理機上,實現併發執行,即利用每個處理機愛處理一個可併發執行的程序。這樣,多個程序便可以同時執行。以此就能提高系統中的資源利用率,增加系統的吞吐量。

進程和線程

進程是指一個內存中運行的應用程序。一個應用程序可以同時啟動多個進程,那麼上面的問題就有了解決的思路:我們啟動兩個進程,一個用來打遊戲,一個用來播放音樂。這當然是一種解決方案,但是想象一下,如果一個應用程序需要執行的任務非常多,例如 LOL 遊戲吧,光是需要播放的音樂就有非常多,人物本身的語音,技能的音效,遊戲的背景音樂,塔攻擊的聲音等等等,還不用說遊戲本身,就光播放音樂就需要創建許多許多的進程,而進程本身是一種非常消耗資源的東西,這樣的設計顯然是不合理的。更何況大多數的操作系統都不需要一個進程訪問其他進程的內存空間,也就是說,進程之間的通信很不方便,此時我們就得引入“線程”這門技術,來解決這個問題。

線程是指進程中的一個執行任務(控制單元),一個進程可以同時併發運行多個線程。我們可以打開任務管理器,觀察到幾乎所有的進程都擁有着許多的「線程」(在 WINDOWS 中線程是默認隱藏的,需要在「查看」裏面點擊「選擇列」,有一個線程數的勾選項,找到並勾選就可以了)。

進程和線程的區別

進程:有獨立的內存空間,進程中的數據存放空間(堆空間和棧空間)是獨立的,至少有一個線程。

線程:堆空間是共享的,棧空間是獨立的,線程消耗的資源也比進程小,相互之間可以影響的,又稱為輕型進程或進程元。

因為一個進程中的多個線程是併發運行的,那麼從微觀角度上考慮也是有先後順序的,那麼哪個線程執行完全取決於 CPU 調度器(JVM 來調度),程序員是控制不了的。我們可以把多線程併發性看作是多個線程在瞬間搶 CPU 資源,誰搶到資源誰就運行,這也造就了多線程的隨機性。下面我們將看到更生動的例子。

Java 程序的進程(Java 的一個程序運行在系統中)里至少包含主線程和垃圾回收線程(後台線程),你可以簡單的這樣認為,但實際上有四個線程(了解就好):

  • [1] main——main 線程,用戶程序入口
  • [2] Reference Handler——清除 Reference 的線程
  • [3] Finalizer——調用對象 finalize 方法的線程
  • [4] Signal Dispatcher——分發處理髮送給 JVM 信號的線程

多線程和單線程的區別和聯繫?

  1. 單核 CPU 中,將 CPU 分為很小的時間片,在每一時刻只能有一個線程在執行,是一種微觀上輪流佔用 CPU 的機制。

  2. 多線程會存在線程上下文切換,會導致程序執行速度變慢,即採用一個擁有兩個線程的進程執行所需要的時間比一個線程的進程執行兩次所需要的時間要多一些。

結論:即採用多線程不會提高程序的執行速度,反而會降低速度,但是對於用戶來說,可以減少用戶的響應時間。

多線程的優勢

儘管面臨很多挑戰,多線程有一些優點仍然使得它一直被使用,而這些優點我們應該了解。

優勢一:資源利用率更好

想象一下,一個應用程序需要從本地文件系統中讀取和處理文件的情景。比方說,從磁盤讀取一個文件需要 5 秒,處理一個文件需要 2 秒。處理兩個文件則需要:

1| 5秒讀取文件A
2| 2秒處理文件A
3| 5秒讀取文件B
4| 2秒處理文件B
5| ---------------------
6| 總共需要14秒

從磁盤中讀取文件的時候,大部分的 CPU 時間用於等待磁盤去讀取數據。在這段時間里,CPU 非常的空閑。它可以做一些別的事情。通過改變操作的順序,就能夠更好的使用 CPU 資源。看下面的順序:

1| 5秒讀取文件A
2| 5秒讀取文件B + 2秒處理文件A
3| 2秒處理文件B
4| ---------------------
5| 總共需要12秒

CPU 等待第一個文件被讀取完。然後開始讀取第二個文件。當第二文件在被讀取的時候,CPU 會去處理第一個文件。記住,在等待磁盤讀取文件的時候,CPU 大部分時間是空閑的。

總的說來,CPU 能夠在等待 IO 的時候做一些其他的事情。這個不一定就是磁盤 IO。它也可以是網絡的 IO,或者用戶輸入。通常情況下,網絡和磁盤的 IO 比 CPU 和內存的 IO 慢的多。

優勢二:程序設計在某些情況下更簡單

在單線程應用程序中,如果你想編寫程序手動處理上面所提到的讀取和處理的順序,你必須記錄每個文件讀取和處理的狀態。相反,你可以啟動兩個線程,每個線程處理一個文件的讀取和操作。線程會在等待磁盤讀取文件的過程中被阻塞。在等待的時候,其他的線程能夠使用 CPU 去處理已經讀取完的文件。其結果就是,磁盤總是在繁忙地讀取不同的文件到內存中。這會帶來磁盤和 CPU 利用率的提升。而且每個線程只需要記錄一個文件,因此這種方式也很容易編程實現。

優勢三:程序響應更快

有時我們會編寫一些較為複雜的代碼(這裏的複雜不是說複雜的算法,而是複雜的業務邏輯),例如,一筆訂單的創建,它包括插入訂單數據、生成訂單趕快找、發送郵件通知賣家和記錄貨品銷售數量等。用戶從單擊“訂購”按鈕開始,就要等待這些操作全部完成才能看到訂購成功的結果。但是這麼多業務操作,如何能夠讓其更快地完成呢?

在上面的場景中,可以使用多線程技術,即將數據一致性不強的操作派發給其他線程處理(也可以使用消息隊列),如生成訂單快照、發送郵件等。這樣做的好處是響應用戶請求的線程能夠盡可能快地處理完成,縮短了響應時間,提升了用戶體驗。

其他優勢

多線程還有一些優勢也顯而易見:

  • 進程之前不能共享內存,而線程之間共享內存(堆內存)則很簡單。
  • 系統創建進程時需要為該進程重新分配系統資源,創建線程則代價小很多,因此實現多任務併發時,多線程效率更高.
  • Java 語言本身內置多線程功能的支持,而不是單純地作為底層系統的調度方式,從而簡化了多線程編程.

上下文切換

即使是單核處理器也支持多線程執行代碼,CPU 通過給每個線程分配 CPU 時間片來實現這個機制。時間片是 CPU 分配給各個線程的時間,因為時間片非常短,所以 CPU 通過不停地切換線程執行,讓我們感覺多個線程是同時執行的,時間片一般是幾十毫秒(ms)。

CPU 通過時間片分配算法來循環執行任務,當前任務執行一個時間片後會切換到下一個任務。但是,在切換前會保存上一個任務的狀態,以便下次切換回這個任務的時候,可以再加載這個任務的狀態。所以任務從保存到再加載的過程就是一次上下文切換。

這就像我們同時讀兩本書,當我們在讀一本英文的技術書時,發現某個單詞不認識,於是打開中英文字典,但是在放下英文技術書之前,大腦必須先記住這本書獨到了多少頁的多少行,等查完單詞之後,能夠繼續讀這本書。這樣的切換是會影響讀書效率的,同樣上下文切換也會影響多線程的執行速度。

二、創建線程的兩種方式

繼承 Thread 類

public class Tester {

    // 播放音樂的線程類
    static class PlayMusicThread extends Thread {

        // 播放時間,用循環來模擬播放的過程
        private int playTime = 50;

        public void run() {
            for (int i = 0; i < playTime; i++) {
                System.out.println("播放音樂" + i);
            }
        }
    }

    // 方式1:繼承 Thread 類
    public static void main(String[] args) {
        // 主線程:運行遊戲
        for (int i = 0; i < 50; i++) {
            System.out.println("打遊戲" + i);
            if (i == 10) {
                // 創建播放音樂線程
                PlayMusicThread musicThread = new PlayMusicThread();
                musicThread.start();
            }
        }
    }
}

運行結果發現打遊戲和播放音樂交替出現,說明已經成功了。

實現 Runnable 接口

public class Tester {

    // 播放音樂的線程類
    static class PlayMusicThread implements Runnable {

        // 播放時間,用循環來模擬播放的過程
        private int playTime = 50;

        public void run() {
            for (int i = 0; i < playTime; i++) {
                System.out.println("播放音樂" + i);
            }
        }
    }

    // 方式2:實現 Runnable 方法
    public static void main(String[] args) {
        // 主線程:運行遊戲
        for (int i = 0; i < 50; i++) {
            System.out.println("打遊戲" + i);
            if (i == 10) {
                // 創建播放音樂線程
                Thread musicThread = new Thread(new PlayMusicThread());
                musicThread.start();
            }
        }
    }
}

也能完成效果。

以上就是傳統的兩種創建線程的方式,事實上還有第三種,我們後邊再講。

多線程一定快嗎?

先來一段代碼,通過并行和串行來分別執行累加操作,分析:下面的代碼併發執行一定比串行執行快嗎?

import org.springframework.util.StopWatch;

// 比較并行和串行執行累加操作的速度
public class Tester {

    // 執行次數
    private static final long COUNT = 100000000;
    private static final StopWatch TIMER = new StopWatch();

    public static void main(String[] args) throws InterruptedException {
        concurrency();
        serial();
        // 打印比較測試結果
        System.out.println(TIMER.prettyPrint());
    }

    private static void serial() {
        TIMER.start("串行執行" + COUNT + "條數據");

        int a = 0;
        for (long i = 0; i < COUNT; i++) {
            a += 5;
        }
        // 串行執行
        int b = 0;
        for (long i = 0; i < COUNT; i++) {
            b--;
        }

        TIMER.stop();
    }

    private static void concurrency() throws InterruptedException {
        TIMER.start("并行執行" + COUNT + "條數據");

        // 通過匿名內部類來創建線程
        Thread thread = new Thread(() -> {
            int a = 0;
            for (long i = 0; i < COUNT; i++) {
                a += 5;
            }
        });
        thread.start();

        // 并行執行
        int b = 0;
        for (long i = 0; i < COUNT; i++) {
            b--;
        }
        // 等待線程結束
        thread.join();
        TIMER.stop();
    }
}

大家可以自己測試一下,每一台機器 CPU 不同測試結果可能也會不同,之前在 WINDOWS 本兒上測試的時候,多線程的優勢從 1 千萬數據的時候才開始體現出來,但是現在換了 MAC,1 億條數據時間也差不多,到 10 億的時候明顯串行就比并行快了… 總之,為什麼併發執行的速度會比串行慢呢?就是因為線程有創建和上下文切換的開銷。

繼承 Thread 類還是實現 Runnable 接口?

想象一個這樣的例子:給出一共 50 個蘋果,讓三個同學一起來吃,並且給蘋果編上號碼,讓他們吃的時候順便要說出蘋果的編號:

運行結果可以看到,使用繼承方式實現,每一個線程都吃了 50 個蘋果。這樣的結果顯而易見:是因為顯式地創建了三個不同的 Person 對象,而每個對象在堆空間中有獨立的區域來保存定義好的 50 個蘋果。

而使用實現方式則滿足要求,這是因為三個線程共享了同一個 Apple 對象,而對象中的 num 數量是一定的。

所以可以簡單總結出繼承方式和實現方式的區別:

繼承方式:

  1. Java 中類是單繼承的,如果繼承了 Thread 了,該類就不能再有其他的直接父類了;
  2. 從操作上分析,繼承方式更簡單,獲取線程名字也簡單..(操作上,更簡單)
  3. 從多線程共享同一個資源上分析,繼承方式不能做到…

實現方式:

  1. Java 中類可以實現多個接口,此時該類還可以繼承其他類,並且還可以實現其他接口(設計上,更優雅)..
  2. 從操作上分析,實現方式稍微複雜點,獲取線程名字也比較複雜,需要使用 Thread.currentThread() 來獲取當前線程的引用..
  3. 從多線程共享同一個資源上分析,實現方式可以做到..

在這裏,三個同學完成搶蘋果的例子,使用實現方式才是更合理的方式。

對於這兩種方式哪種好並沒有一個確定的答案,它們都能滿足要求。就我個人意見,我更傾向於實現 Runnable 接口這種方法。因為線程池可以有效的管理實現了 Runnable 接口的線程,如果線程池滿了,新的線程就會排隊等候執行,直到線程池空閑出來為止。而如果線程是通過實現 Thread 子類實現的,這將會複雜一些。

有時我們要同時融合實現 Runnable 接口和 Thread 子類兩種方式。例如,實現了 Thread 子類的實例可以執行多個實現了 Runnable 接口的線程。一個典型的應用就是線程池。

常見錯誤:調用 run() 方法而非 start() 方法

創建並運行一個線程所犯的常見錯誤是調用線程的 run() 方法而非 start() 方法,如下所示:

1| Thread newThread = new Thread(MyRunnable());
2| newThread.run();  //should be start();

起初你並不會感覺到有什麼不妥,因為 run() 方法的確如你所願的被調用了。但是,事實上,run() 方法並非是由剛創建的新線程所執行的,而是被創建新線程的當前線程所執行了。也就是被執行上面兩行代碼的線程所執行的。想要讓創建的新線程執行 run() 方法,必須調用新線程的 start() 方法。

三、線程的安全問題

吃蘋果遊戲的不安全問題

我們來考慮一下上面吃蘋果的例子,會有什麼問題?

儘管,Java 並不保證線程的順序執行,具有隨機性,但吃蘋果比賽的案例運行多次也並沒有發現什麼太大的問題。這並不是因為程序沒有問題,而只是問題出現的不夠明顯,為了讓問題更加明顯,我們使用 Thread.sleep() 方法(經常用來模擬網絡延遲)來讓線程休息 10 ms,讓其他線程去搶資源。(注意:在程序中並不是使用 Thread.sleep(10)之後,程序才出現問題,而是使用之後,問題更明顯.)

為什麼會出現這樣的錯誤呢?

先來分析第一種錯誤:為什麼會吃重複的蘋果呢?就拿 B 和 C 都吃了編號為 47 的蘋果為例吧:

  • A 線程拿到了編號為 48 的蘋果,打印輸出然後讓 num 減 1,睡眠 10 ms,此時 num 為 47。
  • 這時 B 和 C 同時都拿到了編號為 47 的蘋果,打印輸出,在其中一個線程作出了減一操作的時候,A 線程從睡眠中醒過來,拿到了編號為 46 的蘋果,然後輸出。在這期間並沒有任何操作不允許 B 和 C 線程不能拿到同一個編號的蘋果,之前沒有明顯的錯誤僅僅可能只是因為運行速度太快了。

再來分析第二種錯誤:照理來說只應該存在 1-50 編號的蘋果,可是 0 和-1 是怎麼出現的呢?

  • 當 num = 1 的時候,A,B,C 三個線程同時進入了 try 語句進行睡眠。
  • C 線程先醒過來,輸出了編號為 1 的蘋果,然後讓 num 減一,當 C 線程醒過來的時候發現 num 為 0 了。
  • A 線程醒過來一看,0 都沒有了,只有 -1 了。

歸根結底是因為沒有任何操作來限制線程來獲取相同的資源並對他們進行操作,這就造成了線程安全性問題。

如果我們把打印和減一的操作分成兩個步驟,會更加明顯:

ABC 三個線程同時打印了 50 的蘋果,然後同時做出減一操作。

像這樣的原子操作,是不允許分步驟進行的,必須保證同步進行,不然可能會引發不可設想的後果。

要解決上述多線程併發訪問一個資源的安全性問題,就需要引入線程同步的概念。

線程同步

多個執行線程共享一個資源的情景,是最常見的併發編程情景之一。為了解決訪問共享資源錯誤或數據不一致的問題,人們引入了臨界區的概念:用以訪問共享資源的代碼塊,這個代碼塊在同一時間內只允許一個線程執行。

為了幫助編程人員實現這個臨界區,Java(以及大多數編程語言)提供了同步機制,當一個線程試圖訪問一個臨界區時,它將使用一種同步機制來查看是不是已經有其他線程進入臨界區。如果沒有其他線程進入臨界區,他就可以進入臨界區。如果已經有線程進入了臨界區,它就被同步機制掛起,直到進入的線程離開這個臨界區。如果在等待進入臨界區的線程不止一個,JVM 會選擇其中的一個,其餘的將繼續等待。

synchronized 關鍵字

如果一個對象已用 synchronized 關鍵字聲明,那麼只有一個執行線程被允許訪問它。使用 synchronized 的好處顯而易見:保證了多線程併發訪問時的同步操作,避免線程的安全性問題。但是壞處是:使用 synchronized 的方法/代碼塊的性能比不用要低一些。所以好的做法是:盡量減小 synchronized 的作用域。

我們還是先來解決吃蘋果的問題,考慮一下 synchronized 關鍵字應該加在哪裡呢?

發現如果還再把 synchronized 關鍵字加在 if 裏面的話,0 和 -1 又會出來了。這其實是因為當 ABC 同是進入到 if 語句中,等待臨界區釋放的時,拿到 1 編號的線程已經又把 num 減一操作了,而此時最後一個等待臨界區的進程拿到的就會是 -1 了。

同步鎖 Lock

Lock 機制提供了比 synchronized 代碼塊和 synchronized 方法更廣泛的鎖定操作,同步代碼塊/ 同步方法具有的功能 Lock 都有,除此之外更強大,更體現面向對象。在併發包的類族中,Lock 是 JUC 包的頂層接口,它的實現邏輯並未用到 synchronized,而是利用了 volatile 的可見性。

使用 Lock 最典型的代碼如下:

class X {

    private final ReentrantLock lock = new ReentrantLock();

    public void m() {
        lock.lock();
        try {
            // ..... method body
        } finally {
            lock.unlock();
        }
    }
}

線程安全問題

線程安全問題只在多線程環境下才會出現,單線程串行執行不存在此類問題。保證高併發場景下的線程安全,可以從以下四個維度考量:

維度一:數據單線程可見

單線程總是安全的。通過限制數據僅在單線程內可見,可以避免數據被其他線程篡改。最典型的就是線程局部變量,它存儲在獨立虛擬機棧幀的局部變量表中,與其他線程毫無瓜葛。TreadLocal 就是採用這種方式來實現線程安全的。

維度二:只讀對象

只讀對象總是安全的。它的特性是允許複製、拒絕寫入。最典型的只讀對象有 String、Integer 等。一個對象想要拒絕任何寫入,必須要滿足以下條件:

  • 使用 final 關鍵字修飾類,避免被繼承;
  • 使用 private final 關鍵字避免屬性被中途修改;
  • 沒有任何更新方法;
  • 返回值不能為可變對象。

維度三:線程安全類

某些線程安全類的內部有非常明確的線程安全機制。比如 StringBuffer 就是一個線程安全類,它採用 synchronized 關鍵字來修飾相關方法。

維度四:同步與鎖機制

如果想要對某個對象進行併發更新操作,但又不屬於上述三類,需要開發工程師在代碼中實現安全的同步機制。雖然這個機制支持的併發場景很有價值,但非常複雜且容易出現問題。

處理線程安全的核心理念

要麼只讀,要麼加鎖。

合理利用好 JDK 提供的併發包,往往能化腐朽為神奇。Java 併發包(java.util.concurrent,JUC)中大多數類註釋都寫有:@author Doug Lea。如果說 Java 是一本史書,那麼 Doug Lea 絕對是開疆拓土的偉大人物。Doug Lea 在當大學老師時,專攻併發編程和併發數據結構設計,主導設計了 JUC 併發包,提高了 Java 併發編程的易用性,大大推進了 Java 的商用進程。

參考資料

  • 《Java 零基礎入門教程》 –
  • 《Java 併發編程的藝術》
  • 《Java 7 併發編程實戰手冊》
  • 《碼出高效 Java 開發手冊》 – 楊冠寶(孤盡) 高海慧(鳴莎)著

按照慣例黏一個尾巴:

歡迎轉載,轉載請註明出處!
獨立域名博客:wmyskxz.com
簡書 ID:
github:
歡迎關注公眾微信號:wmyskxz
分享自己的學習 & 學習資料 & 生活
想要交流的朋友也可以加 qq 群:3382693

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※高價收購3C產品,價格不怕你比較

※如何讓商品強力曝光呢? 網頁設計公司幫您建置最吸引人的網站,提高曝光率!!

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

3c收購,鏡頭 收購有可能以全新價回收嗎?

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

【前端知識體系-JS相關】10分鐘搞定JavaScript正則表達式高頻考點

1.正則表達式基礎

1.1 創建正則表達式

1.1.1 使用一個正則表達式字面量

const regex = /^[a-zA-Z]+[0-9]*\W?_$/gi;

1.1.2 調用RegExp對象的構造函數

const regex = new RegExp(pattern, [, flags])

1.1.3 特殊字符

 - ^ 匹配輸入的開始
 - $ 匹配輸入的結束
 - \* 0次或多次  {0,}
 - \+ 1次或多次  {1,}
 - ?
   - 0次或者1次 {0,1}。
   - 用於先行斷言
   - 如果緊跟在任何量詞 *、 +、? 或 {} 的後面,將會使量詞變為非貪婪
     - 對 "123abc" 用 /\d+/ 將會返回 "123",
     - 用 /\d+?/,那麼就只會匹配到 "1"。
 - . 匹配除換行符之外的任何單個字符
 - (x)  匹配 'x' 並且記住匹配項
 - (?:x)  匹配 'x' 但是不記住匹配項
 - x(?=y)  配'x'僅僅當'x'後面跟着'y'.這種叫做正向肯定查找。
 - x(?!y)  匹配'x'僅僅當'x'後面不跟着'y',這個叫做正向否定查找。
 - x|y  匹配‘x’或者‘y’。
 - {n}  重複n次
 - {n, m}  匹配至少n次,最多m次
 - [xyz]   代表 x 或 y 或 z
 - [^xyz]  不是 x 或 y 或 z
 - \d  数字
 - \D  非数字
 - \s  空白字符,包括空格、製表符、換頁符和換行符。
 - \S  非空白字符
 - \w  單詞字符(字母、数字或者下劃線)  [A-Za-z0-9_]
 - \W  非單字字符。[^A-Za-z0-9_]
 - \3  表示第三個分組
 - \b   詞的邊界
   - /\bm/匹配“moon”中得‘m’;
 - \B   非單詞邊界

1.2 使用正則表達式的方法

  • exec 一個在字符串中執行查找匹配的RegExp方法,它返回一個數組(未匹配到則返回null)。
  • test 一個在字符串中測試是否匹配的RegExp方法,它返回true或false。
  • match 一個在字符串中執行查找匹配的String方法,它返回一個數組或者在未匹配到時返回null。
  • search 一個在字符串中測試匹配的String方法,它返回匹配到的位置索引,或者在失敗時返回-1。
  • replace 一個在字符串中執行查找匹配的String方法,並且使用替換字符串替換掉匹配到的子字符串。
  • split 一個使用正則表達式或者一個固定字符串分隔一個字符串,並將分隔后的子字符串存儲到數組中的String方法。

1.2.1 正則對象的三個方法

        //①test()判斷字符串中是否出現某個字符串,返回布爾值
        var re = /abc/;
        var str = '00abc66';
        console.log(re.test(str));  // true
        //②exec()查找並返回字符串中指定的某個字符串,只匹配一次
        var re = /abc/;
        var str = 'a0bc88abc00abc';
        console.log(re.exec(str));  // ["abc", index: 6, input: "a0bc88abc00abc", groups: undefined]
        //③compile()方法用於改變正則匹配的內容
        var re = /ab/;
        var str = "aabcdef";
        console.log(re.test(str));  //true
        re.compile(/bd/);
        console.log(re.test(str));  //false
        re.compile('66');
        console.log(re.test(str));  //false

1.2.2 字符串中與正則相關的方法

        //①search()方法,返回符合條件的字符串首次出現的位置(下標)
        var re = /abc/;
        var str = '00abc66';
        console.log(str.search(re));        // 2
        //②match()方法,返回查找的結果,如果查詢不到返回NULL
        console.log(str.match(re));         // ["abc", index: 2, input: "00abc66", groups: undefined]
        //③replace()方法,將匹配到的內容替換成指定內容
        console.log(str.replace(re, "*"));
        //④split()方法,將字符串分割成字符串數組
        console.log(str.split(re));

1.3 正則表達式子表達式相關

1.3.1 子表達式

在正則表達式中,通過一對圓括號括起來的內容,我們就稱之為“子表達式”。如:var re = /\d(\d)\d/;

1.3.2 捕獲

在正則表達式中,子表達式匹配到相應的內容時,系統會自動捕獲這個行為,然後將子表達式匹配到的內容放入系統的緩存區中。我們把這個過程就稱之為“捕獲”。

1.3.3 反向引用

在正則表達式中,我們可以使用\n(n>0,正整數,代表系統中的緩衝區編號)來獲取緩衝區中的內容,我們把這個過程就稱之為“反向引用”。

    var str = "d1122jj7667h6868s9999";
    //查找AABB型的数字
    console.log(str.match(/(\d)\1(\d)\2/)); //1122
    //查找ABBA型的数字
    console.log(str.match(/(\d)(\d)\2\1/)); //7667
    //查找ABAB型的数字
    console.log(str.match(/(\d)(\d)\1\2/)); //6868
    //查找四個連續相同的数字
    console.log(str.match(/(\d)\1\1\1/));   //9999

1.4 限定符

[!NOTE]
限定符可以指定正則表達式的一個給定字符必須要出現多少次才能滿足匹配。

    *:匹配前面的子表達式零次或多次,0到多
    +:匹配前面的子表達式一次或多次,1到多
    ?:匹配前面的子表達式零次或一次,0或1
    {n}:匹配確定的 n 次 
    {n,}:至少匹配 n 次 
    {n,m}:最少匹配 n 次且最多匹配 m 次

[!WARNING]
注意:針對於{n,m},正則在匹配到一個符合多種次數的字符串時,優先匹配次數多的,即能匹配到m次就不會匹配n次,這就是貪婪模式(默認)。

如果在其後加?即{n,m}?則會更改為非貪婪模式(惰性模式),則此時正則優先匹配n次。

    var str = "aa1a22a333a6a8a";
    console.log(str.match(/a\d*/));      //a
    console.log(str.match(/a\d+/));      //a1
    console.log(str.match(/a\d?/));      //a
    console.log(str.match(/a\d{3}/));    //a333
    console.log(str.match(/a\d{2,}/));   //a22
    console.log(str.match(/a\d{1,3}/));  //a1
    console.log(str.match(/a\d{1,3}?/)); //a1

    //貪婪模式加深理解,案例如下:
    //貪婪模式下最開始的'a2就符合條件',但是它會返回'a22'
    //注意:它是在遇到一個同時符合多個次數條件的字符串時,取符合次數多字符串
    var str = "a22aa1a333a6a8a";
    console.log(str.match(/a\d{1,3}/));   //a22
    console.log(str.match(/a\d{1,3}/g));  //a22 a1 a333 a6 a8
    console.log(str.match(/a\d{1,3}?/));  //a2
    console.log(str.match(/a\d{1,3}?/g)); //a2 a1 a3 a6 a8

1.5 定位符

[!NOTE]
定位符可以將一個正則表達式固定在一行的開始或結束。也可以創建只在單詞內或只在單詞的開始或結尾處出現的正則表達式。

^ (脫字符):匹配輸入字符串的開始位置
$:匹配輸入字符串的結束位置
\b:匹配一個單詞邊界
\B:匹配非單詞邊界

1.6 正則表達式的匹配模式(修飾符)

[!NOTE]
表示正則匹配的附加規則,放在正則模式的最尾部。修飾符可以單個使用,也可以多個一起使用。

  • ①g全局匹配,找到所有匹配,而不是在第一個匹配后停止
  • ②i匹配全部大小寫
  • ③m多行,將開始和結束字符(^和$)視為在多行上工作(也就是,分別匹配每一行的開始和結束(由\n或\r分割),而不只是只匹配整個輸入字符串的最開始和最末尾處。
  • ④s與m相反,單行匹配
    var re = /^[a-z]/gim;   //可組合使用

1.7 轉義字符

[!NOTE]
因為在正則表達式中 . +  等屬於表達式的一部分,但有時也需要匹配這些特殊字符,所以,需要使用反斜杠對特殊字符進行轉義。

  需要轉義的字符:
  點號.
  小括號()
  中括號[]
  左斜杠/
  右斜杠\
  選擇匹配符|
  
  * 
  ?
  {}
  + 
  $
  ^

2. 正則練習題

2.1 匹配結尾的数字

/\d+$/g

2.2 統計空格個數

字符串內如有空格,但是空格的數量可能不一致,通過正則將空格的個數統一變為一個。

let reg = /\s+/g
str.replace(reg, " ");

2.3 判斷字符串是不是由数字組成

str.test(/^\d+$/);

2.4 電話號碼正則

  • 區號必填為3-4位的数字
  • 區號之後用“-”與電話號碼連接電話號碼為7-8位的数字
  • 分機號碼為3-4位的数字,非必填,但若填寫則以“-”與電話號碼相連接
/^\d{3,4}-\d{7,8}(-\d{3,4})?$/

2.5 手機號碼正則表達式

正則驗證手機號,忽略前面的0,支持130-139,150-159。忽略前面0之後判斷它是11位的。

/^0*1(3|5)\d{9}$/

2.6 使用正則表達式實現刪除字符串中的空格

funtion trim(str) {
  let reg = /^\s+|\s+$/g
  return str.replace(reg, '');
}

2.7 限制文本框只能輸入数字和兩位小數點等等

/^\d*\.\d{0,2}$/

2.8 只能輸入小寫的英文字母和小數點,和冒號,正反斜杠(:./)

/^[a-z\.:\/\\]*$/

2.9 替換小數點前內容為指定內容

例如:infomarket.php?id=197 替換為 test.php?id=197

var reg = /^[^\.]+/;
var target = '---------';
str = str.replace(reg, target)

2.10 只匹配中文的正則表達式

/[\u4E00-\u9FA5\uf900-\ufa2d]/ig

2.11 返回字符串的中文字符個數

先去掉非中文字符,再返回length屬性。

function cLength(str){
  var reg = /[^\u4E00-\u9FA5\uf900-\ufa2d]/g;
  //匹配非中文的正則表達式
  var temp = str.replace(reg,'');
  return temp.length;
}

2.12 正則表達式取得匹配IP地址前三段

只要匹配掉最後一段並且替換為空字符串就行了

function getPreThrstr(str) {
  let reg = /\.\d{1,3}$/;
  return str.replace(reg,'');
}

2.13 匹配ul標籤之間的內容

/<ul>[\s\S]+?</ul>/i

2.14 用正則表達式獲得文件名

c:\images\tupian\006.jpg

可能是直接在盤符根目錄下,也可能在好幾層目錄下,要求替換到只剩文件名。
首先匹配非左右斜線字符0或多個,然後是左右斜線一個或者多個。

function getFileName(str){
  var reg = /[^\\\/]*[\\\/]+/g;
  // xxx\ 或是 xxx/
  str = str.replace(reg,'');
  return str;
}

2.15 絕對路徑變相對路徑

“http://23.123.22.12/image/somepic.gif”轉換為:”/image/somepic.gif”

var reg = /http:\/\/[^\/]+/;
str = str.replace(reg,"");

2.16 用戶名正則

用於用戶名註冊,,用戶名只 能用 中文、英文、数字、下劃線、4-16個字符。

/^[\u4E00-\u9FA5\uf900-\ufa2d\w]{4,16}$/

2.17 匹配英文地址

規則如下:
包含 “點”, “字母”,”空格”,”逗號”,”数字”,但開頭和結尾不能是除字母外任何字符。

/^[a-zA-Z][\.a-zA-Z,0-9]*[a-zA-Z]$/

2.18 正則匹配價格

開頭数字若干位,可能有一個小數點,小數點後面可以有兩位数字。

/^\d+(\.\d{2})?$/

2.19 身份證號碼的匹配

身份證號碼可以是15位或者是18位,其中最後一位可以是X。其它全是数字

/^(\d{14}|\d{17})(X|x)$/

2.20 單詞首字母大寫

每單詞首字大寫,其他小寫。如blue idea轉換為Blue Idea,BLUE IDEA也轉換為Blue Idea

function firstCharUpper(str) {
  str = str.toLowerCase();
  let reg = /\b(\w)/g;
  return str.replace(reg, m => m.toUpperCase());
}

2.21 正則驗證日期格式

yyyy-mm-dd格式, 4位数字,橫線,1或者2位数字,再橫線,最後又是1或者2位数字。

/^\d{4}-\d{1,2}-\d{1,2}$/

2.22 去掉文件的後綴名

www.abc.com/dc/fda.asp 變為 www.abc.com/dc/fda

function removeExp(str) {
  return str.replace(/\.\w$/,'')
}

2.23 驗證郵箱的正則表達式

開始必須是一個或者多個單詞字符或者是-,加上@,然後又是一個或者多個單詞字符或者是-。然後是點“.”和單詞字符和-的組合,可以有一個或者多個組合。

/^[\w-]+@\w+\.\w+$/

2.24 正則判斷標籤是否閉合

標籤可能有兩種方式閉合,自閉和或者對稱閉合的方式。

/<([a-z]+)(\s*\w*?\s*=\s*".+?")*(\s*?>[\s\S]*?(<\/\1>)+|\s*\/>)/i

2.25 正則判斷是否為数字與字母的混合

不能小於12位,且必須為字母和数字的混合

/^(([a-z]+[0-9]+)|([0-9]+[a-z]+))[a-z0-9]*$/i

2.26 將阿拉伯数字替換為中文大寫形式

function replaceReg(reg,str){
  let arr=["零","壹","貳","叄","肆","伍","陸","柒","捌","玖"];
  let reg = /\d/g;
  return str.replace(reg,function(m){return arr[m];})
}

2.27 去掉標籤的所有屬性

<td style="width: 23px; height: 26px;" align="left">***</td>
變成沒有任何屬性的
<td>***</td>

思路:非捕獲匹配屬性,捕獲匹配標籤,使用捕獲結果替換掉字符串。正則如下:

/(<td)\s(?:\s*\w*?\s*=\s*".+?")*?\s*?(>)/

2.28 駝峰表示

String.prototype.camelCase = function () {
        // .*?是非貪婪的匹配,點可以匹配任意字符,星號是前邊的字符有0-n個均匹配,問號是則是0-1;
        // (^\w{1}): 用於匹配第一個首字母
        // (.*):用於匹配任意個的前面的字符,.表示的就是任意字符

        // - param 1: 匹配到的字符串
        // - param 2: 匹配的的子字符串
        // - param 3: 匹配的子字符串
        // - param的位置
        // - param 5: 原始字符串 4: 匹配到的字符串在字符串中

        return this.replace(/(^\w{1})(.*)/g, function (match, g1, g2) {
            return g1.toUpperCase() + g2.toLowerCase();
        });
    }

2.29 模板字符串

// str = 'name: @(name), age:@(age)'
       // data = {name : 'xiugang', age : 18}
       /**
        * 實現一個簡單的數據綁定
        * @param str
        * @param data
        * @return {*}
        */
       String.prototype.formateString = function (data) {
           return this.replace(/@\((\w+)\)/g, function (match, key) {
               // 注意這裏找到的值必須返回出去(如果是undefined,就是沒有數據)
               // 注意:判斷一個值的類型是不是undefined,可以通過typeof判斷
               console.log(typeof data[key] === 'undefined');
               return data[key] === 'undefined' ? '' : data[key];
           });

       }

2.30 去掉兩邊的空格

/**
        * 去掉兩邊的空格
        * @param str
        * @return {*}
        */
       String.prototype.trim = function () {
           return this.replace(/(^\s*)|(\s*$)/g, '');
       }

2.31 獲取url參數: 使用replace保存到一個數組裡面,然後從數組裡面取出數據

'http://www.189dg.com/ajax/sms_query.ashx?undefined&undefined&undefined-06-27&undefined-06-27'
 url.replace(/(\w+)=(\w+)/g, function(a, b, c){
   console.log(a, b, c)
 })
action=smsdetail action smsdetail
sid=22 sid 22
stime=2014 stime 2014
etime=2014 etime 2014


// 封裝為一個函數
var url = "http://127.0.0.1/e/action/ShowInfo.php?classid=9&id=2";
function parse_url(_url){
 var pattern = /(\w+)=(\w+)/ig;
 var parames = {};
 url.replace(pattern, function(a, b, c){
   parames[b] = c;
 });
 return parames;
}
var parames = parse_url(url);
alert(parames['classid'] + ", " + parames['id']);

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

※為什麼 USB CONNECTOR 是電子產業重要的元件?

收購3c,收購IPHONE,收購蘋果電腦-詳細收購流程一覽表

網頁設計一頭霧水??該從何著手呢? 找到專業技術的網頁設計公司,幫您輕鬆架站!

※想要讓你的商品在網路上成為最夯、最多人討論的話題?

※高價收購3C產品,價格不怕你比較

※想知道最厲害的台北網頁設計公司推薦台中網頁設計公司推薦專業設計師”嚨底家”!!

ASP.NET Aries 高級開發教程:如何寫WebAPI接口

前提:

最近,有不少同學又問到,Aries里如何提供WebAPI接口?

針對這個問題,今天給順路寫個教程,其實呢,很簡單的。

方式一:直接用WebService提供接口。

用這種方式,直接添加接口就可以了,Aries只是默認處理了.html後綴的請求。對於WS的asmx後綴是沒有影響的,所以傳統怎麼添加就怎麼添加。

 

方式二:單獨用Taurus.MVC寫一個接口項目。

用這種方式,就是把接口獨立成一個項目,然後通過IIS部署成子應用程序就可以了。

只是部署成子應用程序的時候,需要注意一下子目錄和根目錄的web.config,出現重複的只能留根目錄的那個。

(一般都會建議用戶用這種方式,好處是可以在IIS里學會一下怎麼部署子應用程序。)

方式三:在Aries引入Taurus.MVC即可。

這個方式,其實也很簡單,下面介紹一下簡單的部署:

1、引用Taurus.MVC用於寫WebAPI:

在Web.UI項目添加引用Taurus.Core.dll(可以在Nuget上引用,也可以引用源碼項目再引用項目)

2、配置Taurus.MVC的兩個必備項:

A、在HttpModule中添加URL攔截。

//這是原來有的: 
<add name="Aries.Core" type="Aries.Core.UrlRewrite,Aries.Core" />
//這是新添加的:
 <add name="Taurus.Core" type="Taurus.Core.UrlRewrite,Taurus.Core"/>

B、在AppSetting中設置路徑模式:

<!--配置模式【值為0,1或2】[默認為1]
      值為0:匹配{Action}/{Para}
      值為1:匹配{Controller}/{Action}/{Para}
      值為2:匹配{Module}/{Controller}/{Action}/{Para}-->
    <add key="Taurus.RouteMode" value="1"/>

C、在AppSetting中設置接口代碼所在的項目:

<!--指定控制器所在的項目(Dll)名稱(可改,項目編繹的dll叫什麼名就寫什麼)
    <add key="Taurus.Controllers" value="Taurus.Controllers"/>-->

 

如果是用Nuget上引用的,默認都會有上面的兩個,其它默認生的,可以註釋掉。

3、開始寫應用接口代碼:

接口代碼寫在哪裡呢?放在哪個項目都可以,只要上面C點的配置指向就可以了,如果接口代碼分散在多個項目中,配置的value可以用“逗號”分隔。

按Taurus.MVC的方式寫接口,繼承自Taurus.Core.Controller即可:

如:

  /// <summary>
    /// API 接口
    /// </summary>
    public  class APIController : Taurus.Core.Controller
    {
        public void Hello()
        {
            Write("hello Controllers.API");
        }
    }

接口訪問:http://…/api/hello

總結說明:

Aries中默認處理的是.html後綴。

Taurus默認處理的是無後綴。

所以兩者並無衝突,直接引用,加配置就可以了,沒你想的複雜。

本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理【其他文章推薦】

USB CONNECTOR掌控什麼技術要點? 帶您認識其相關發展及效能

※高價3c回收,收購空拍機,收購鏡頭,收購 MACBOOK-更多收購平台討論專區

※評比前十大台北網頁設計台北網站設計公司知名案例作品心得分享

收購3c瘋!各款手機、筆電、相機、平板,歡迎來詢價!

※智慧手機時代的來臨,RWD網頁設計已成為網頁設計推薦首選