標籤: 網頁設計

  • Java多線程與併發基礎面試題

    CS-LogN思維導圖:記錄專業基礎 面試題
    開源地址:https://github.com/FISHers6/CS-LogN

    多線程與併發基礎

    實現多線程

    面試題1:有幾種實現線程的方法,分別是什麼

    • 1.繼承Thread類,啟動線程的唯一方法就是通過 Thread 類的 start()實例方法,start()方法是一個 native 方法,它將啟動一個新線程去執行 run()方法

    • 2.實現 Runnable 接口,重寫run()函數,作為參數放到Thread類構造函數中作為target屬性,運行start()方法

    • 線程池創建線程、Callable本質還是使Runnable創建,Callable是父輩類繼承了Runnable,線程池需傳入參數

    面試題2:實現Runnable方法好,還是繼承Thread類好

    • 實現Runnable接口更好

      • 1.單一繼承原則,如果繼承了Thread,就不能繼承其它類了,限制了可擴展性
      • 2.Thread類每次只能創建一個獨立的線程,損耗大,而Runnable能利用線程池工具來創建線程
      • 3.從代碼架構上看,run內容應該與Trhead代碼解耦

    面試題3:一個線程兩次調用start方法會出現什麼情況(考察源碼)

    • 第二次會出現異常,從start源碼上和線程生命周期上分析,一個線程start后,
      改變了threadState狀態字;而第二次再start每次會先檢查這個狀態不是0就報異常

    面試題4:既然start方法會調用run方法,為什麼我們還是要用start方法,而不是直接調用run方法呢(考察源碼)

    • 因為start后線程才會經過完整的線程生命周期,start調用native start0,虛擬機執startThread,thread_entry入口中調用Thread的run,

    面試題5:start和run有什麼區別

    • run()方法:只是普通的方法,調用run普通方法,可以重複多次調用
    • start()方法,會啟動一個線程,使得虛擬機去調用Runnable對象的run()方法,不能多次啟動同一個線程

    面試題6:start方法如何調用run方法的(考察源碼和JVM)

    • start方法調用native start0,JVM虛擬機執行startThread,在thread_entry中調用Thread的run方法

    面試題7:如何正確停止線程

    • 使用interrupt中斷通知,而不是強制,中斷通知後會讓被停止線程去決定何時停止,即把主動權交給需要被中斷的線程

    線程的生命周期

    面試題1:Java線程有哪幾種狀態 說說生命周期

    • 六種生命狀態(若time_waiting也算一種)

      • New,已創建但還尚未啟動的新線程
      • Runable,可運行狀態;對應操作系統的兩種狀態“就緒態” 和 “運行態”(分配到CPU)
      • Blocked,阻塞狀態;請求synchronized鎖未分配到時阻塞,直到獲取到monitor鎖再進入Runnable
      • Waiting,等待狀態
      • Timed waiting,限期等待
      • Terminated終止狀態
    • 線程的生命周期 狀態轉換圖

    Thread和Object類中

    與線程相關的重要方法

    面試題1:實現兩個線程交替打印奇數偶數

    面試題2:手寫生產者消費者設計模式,為什麼用該模式

    • 主要是為了解耦,匹配不同的能力

    面試題3:wait后發生了什麼,為什麼需要在同步代碼內才能使用

    • 從jvm的源碼實現上看,wait后,線程讓出佔有的cpu並釋放同步資源鎖;把自己加入到等待池,以後不會再主動參与cpu的競爭,除非被其它notify命中
    • 為了確保線程安全;另外wait會釋放資源,所以肯定要先拿到這個鎖,能進入同步代碼塊已經拿到了鎖

    面試題4:為什麼線程通信的方法wait,notify和notifyAll放在Object類,而sleep定義在Thread類里 (考察對象鎖)

    • 與對象的鎖有關,對象鎖綁定在對象的對象頭中,且放在Object里,使每個線程都可以持有多個對象的鎖

    面試題5:wait方法是屬於Object對象的,那調用Thread.wait會怎麼樣

    • 線程死的時候會自己notifyAll,釋放掉所有的持有自己對象的鎖。這個機制是實現很多同步方法的基礎。如果調用Thrad.wait,干擾了我們設計的同步業務流程

    面試題6:如何選擇notify還是notifyAll

    • 優先選用notifyAll,喚醒所有線程;除非業務需要每次只喚醒一個線程的

    面試題7:notfiy后發生的操作,notifyAll之後所有的線程都會再次搶奪鎖,如果某線程搶奪失敗怎麼辦?

    • notify后,讓waiterSet等待池中的一個線程與entry_List鎖池一級活躍線程一起競爭CPU
    • 搶奪鎖失敗後會繼續待在原鎖池或原等待池,等待競爭CPU的調度

    面試題8:sleep方法與notify/wait方法的異同點

    • 相同點:線程都會進入waiting狀態,都可以響應中斷
    • 不同點:1.所屬類不同;2.wait/notify必須用在同步方法中,且會釋放鎖;3.sleep可以指定時間

    面試題9:join方法後父線程進入什麼狀態

    • waiting狀態,join內部調用wait,子線程結束后自動調用notifyAll喚醒(jvm:exit函數)

    線程安全與性能

    面試題1:守護線程和普通線程的區別

    • 守護線程是服務於普通線程的,並且不會影響到jvm的退出

    面試題2:什麼是線程安全

    • 不管業務中遇到怎樣的多個線程訪問某對象或某方法的情況,而在編程這個業務邏輯的時候,都不需要再額外做任何額外的處理(也就是可以像單線程編程一樣),程序也可以正常運行(不會因為多線程而出錯),就可以稱為線程安全

    面試題3:有哪些線程不安全的情況,什麼原因導致的

    • 1.數據爭用、同時操作,如數據讀寫由於同時寫,非原子性操作導致運行結果錯誤,a++
    • 2.存在競爭,順序不當,如死鎖、活鎖、飢餓

    面試題4:什麼是多線程的上下文切換,及導致的後果

    • 進程線程切換要保存所需要的CPU運行環境,如寄存器、棧、全局變量等資源
    • 在頻繁的io以及搶鎖的時候,會導緻密集的上下文切換,多線程切換時,由於緩存和上下文的切換會帶來性能問題

    面試題5:多線程導致的開銷有哪些

    • 1.上下文切換開銷,如保存緩存(cache、快表等)的開銷

    • 2.同步協作的開銷(java內存模型)

      • 為了數據的正確性,同步手段往往會使用禁止編譯器優化(如指令重排序優化、鎖粗化等),性能變差
      • 使CPU內的緩存失效(比如volatile可見性讓自己線程的緩存失效后,必須使用主存來查看數據)

    Java內存模型

    面試題1:Java的代碼如何一步步轉化,最終被CPU執行的

      1. 最開始,我們編寫的Java代碼,是*.java文件
    1. 在編譯(javac命令)后,從剛才的.java文件會變出一個新的Java字節碼文件.class
    2. JVM會執行剛才生成的字節碼文件(*.class),並把字節碼文件轉化為機器指令
    3. 機器指令可以直接在CPU上執運行,也就是最終的程序執行
    • JVM實現會帶來不同的“翻譯”,不同的CPU平台的機器指令又千差萬別,無法保證併發安全的效果一致

    面試題2:單例模式的作用和適用場景

    • 單例模式:只獲取一次資源,全程序通用,節省內存和計算;保證多線程計算結果正確;方便管理;
      比如日期工具類只需要一個實例就可以,無需多個示例

    面試題3:單例模式的寫法,考察(重排序、單例和高併發的關係)

    • 餓漢式(靜態常量、靜態代碼塊)

      • 原理1:static靜態常量在類加載的時候就初始化完成了,且由jvm保證線程安全,保證了變量唯一
      • 原理2:靜態代碼塊中實例化和靜態常量類似;放在靜態代碼塊里初始化,類加載時完成;
      • 特徵:簡單,但沒有懶加載(需要時再加載)
    • 懶漢式(加synchronized鎖)

      • 對初始化的方法加synchronized鎖達到線程安全的目的,但效率低,多線程下變成了同步
      • 懶漢式取名:用到的時候才去加載
    • 雙重檢查

      • 代碼實現

        • 屬性加volatile,兩次if判斷NULL值,第二次前加類鎖
      • 優點

        • 線程安全;延遲加載;效率高
      • 為什麼用雙重而不用單層

        • 從線程安全方面、效率方面講
    • 靜態內部類

      • 需要理解靜態內部類的優點,懶漢式加載,jvm加載順序
    • 枚舉

      • 代碼實現簡單

        • public enum Singleton{
          INSTANCE;
          public void method(){}
          }
      • 保證了線程安全

        • 枚舉是一個特殊的類,經過反編譯查看,枚舉最終被編譯成一個final的類,繼承了枚舉父類。各個實例通過static定義,本質就是一個靜態的對象,所有第一次使用的時候採取加載(懶加載)
      • 避免反序列化破壞單例

        • 避免了:比如用反射就繞過了構造方法,反序列化出多個實例

    面試題4:單例模式各種寫法分別適用的場合

    • 1.最好的方法是枚舉,因枚舉被編譯成final類,用static定義靜態對象,懶加載。既保證了線程安全又避免了反序列化破壞單例
    • 2.如果程序一開始要加載的資源太多,考慮到啟動速度,就應該使用懶加載
    • 3.如果是對象的創建需要配置文件(一開始要加載其它資源),就不適合用餓漢式

    面試題5:餓漢式單例的缺點

    • 沒有懶加載(初始化時全部加載出),初始化開銷大

    面試題6:懶漢式單例的缺點

    • 雖然用到的時候才去加載,但是由於加鎖,性能低

    面試題7:單例模式的雙重檢查寫法為什麼要用double-check

    • 從代碼實現出發,保證線程安全、延遲加載效率高

    面試題8:為什麼雙重檢查要用volatile

    • 1.保證instance的可見性

      • 類初始化分成3條指令,重排序帶來NPE空虛指針問題,加volatile防止重排序
    • 2.防止初始化指令重排序

    面試題9:講一講什麼是Java的內存模型

    • 1.是一組規範,需要JVM實現遵守這個規範,以便實現安全的多線程程序
      2.volatile、synchronized、Lock等同步工具和關鍵字實現原理都用到了JMM
      3.重排序、內存可見性、原子性

    面試題10:什麼是happens-before,規則有哪些

    • 解決可見性問題的:在時間上,動作A發生在動作B之前,B保證能看見A,這就是happens-before

    • 規則

      • 1 單線程按代碼順序規則;2 鎖操作(synchronized和Lock);3volatile變量;4.JUC工具類的Happens-Before原則
      • 5.線程啟動時子線程啟動前能看到主線程run的所有內容;6.線程join主線程一定要等待子線程完成后再去做後面操作
      • 7.傳遞性 8.中斷檢測 9.對象構造方法的最後一行指令 happens-before 於 finalize() 方法的第一行指令

    面試題11:講一講volatile關鍵字

    • volatile是一種同步機制,比synchronized或者Lock相關類更輕量,因為使用volatile並不會發生上下文切換等開銷很大的行為。而加鎖時對象鎖會阻塞開銷大。
    • 可見性,如果一個變量別修飾成volatile,那麼JVM就知道了這個變量可能會被併發修改;
    • 不能保證原子性

    面試題12:volatile的適用場合及作用

    • 作用

      • 1.保證可見性 2.禁止指令重排序(單例雙重鎖時)
    • 適合場景

      • 適用場合1:boolean flag,布爾具有原子性,可再由volatile保證其可見性
      • 適用場合2:作為刷新之前變量的觸發器
      • 但不適合非原子性操作如:a++等

    面試題13:volatile和synchronized的異同

    • 1 性能開銷方面: 鎖開銷更大,volatile無加鎖阻塞開銷
      2 作用方面:volatile只能保證可見性,鎖既能保證可見性,又能保證原子性

    面試題14:什麼是內存可見性問題,為什麼存在

    • 多線程下,一個線程修改共享數據后,其它線程能否感知到修改了數據的線程的變化
    • CPU有多級緩存,導致讀的數據過期,各處理機有獨自的緩存未及時更新時,與主存內容不一致

    面試題15:主內存和本地內存的關係是什麼

    • Java 作為高級語言,屏蔽了CPU cache等底層細節,用 JMM 定義了一套讀寫內存數據的規範,雖然我們不再需要關心一級緩存和二級緩存的問題,但是,JMM 抽象了主內存和本地內存的概念。
    • 線程擁有自己的本地內存,並共享主內存的數據;線程讀寫共享數據也是通過本地內存交換的,所以才導致了可見性問題。

    面試題16:什麼是原子操作,Java的原子操作有哪些

    • 原子操作

      • 一系列的操作,要麼全部執行成功,要麼全部不執行,不會出現執行一半的情況,是不可分割的。
    • 1)除long和double之外的基本類型(int, byte, boolean, short, char, float)的”賦值操作”

    • 2)所有”引用reference的賦值操作”,不管是 32 位的機器還是 64 位的機器

    • 3)java.concurrent.Atomic.* 包中所有類的原子操作

    面試題17:long 和 double 的原子性你了解嗎

    • 在32位上的JVM上,long 和 double的操作不是原子的,但是在64位的JVM上是原子的。
    • 在32位機器上一次只能讀寫32位;而浮點數、long型有8字節64位;要分高32位和低32位兩條指令分開寫入,類似彙編語言浮點數乘法分高低位寄存器;64位不用分兩次讀寫了

    面試題18:生成對象的過程是不是原子操作

    • 不是,對象生成會生成分配空間、初始化、賦值,三條指令,有可能會被重排序,導致空指針

    面試題19:區分JVM內存結構、Java內存模型 、Java對象模型

    • Java內存模型,和Java的併發編程有關

      • 詳見面試題9
    • JVM內存結構,和Java虛擬機的運行時區域(堆棧)有關

      • 堆區、方法區(存放常量池 引用 類信息)
        棧區、本地方法棧、程序計數器

    • Java對象模型,和Java對象在虛擬機中的表現形式有關

      • 是Java對象自身的存儲模型,在方法區中Kclass類信息(虛函數表),在堆中存放new實例,在線程棧中存放引用,OOP-Klass Model

    面試題20:什麼是重排序

    • 指令實際執行順序和代碼在java文件中的順序不一致
    • 重排序的好處:提高處理速度,包括編譯器優化、指令重排序(局部性原理)

    死鎖

    面試題1:寫一個必然死鎖的例子

    • synchronized嵌套,構成請求循環

    面試題2:生產中什麼場景下會發生死鎖

    • 併發中多線程互不相讓:當兩個(或更多)線程(或進程)相互持有對方所需要的資源,又不主動釋放,導致所有人都無法繼續前進,導致程序陷入無盡的阻塞,這就是死鎖。

    面試題3:發生死鎖必須滿足哪些條件

    • 1.互斥
    • 2.請求和保持
    • 3.不可剝奪
    • 4.存儲循環等待鏈

    面試題4:如何用工具定位死鎖

    • 1.jstack命令在程序發生死鎖后,進行堆棧分析出死鎖線程
    • 2.ThreadMXbean 程序運行中發現死鎖,一旦發現死鎖可以讓用戶去打日誌

    面試題5:有哪些解決死鎖問題的策略

    • 1.死鎖語法,不讓死鎖發生

      • 破壞死鎖的四個條件之一;如:哲學家換手、轉賬換序
    • 2.死鎖避免

      • 銀行家算法、系統安全序列
    • 3.死鎖檢查與恢復

      • 適用資源請求分配圖,一段時間內檢查死鎖,有死鎖就恢復策略,採用恢復策略;
      • 恢復方法:進程終止 、資源剝奪
    • 4.鴕鳥策略(忽略死鎖)

      • 先忽略,後期再讓人工恢復

    面試題6:死鎖避免策略和檢測與恢復策略的主要思路是什麼

    • 死鎖語法

      • 破壞死鎖的四大條件之一
    • 死鎖避免

      • 找到安全序列,銀行家算法
    • 死鎖檢測與恢復

      • 資源請求分配圖

    面試題7:講一講經典的哲學家就餐問題,如何解決死鎖

    • 什麼時候死鎖

      • 哲學家各拿起自己左手邊的筷子,又去請求拿右手邊筷子循環請求時而阻塞
    • 如何解決死鎖

      • 1.一次兩隻筷子,形成原子性操作
      • 2.只允許4個人拿有筷子

    面試題8:實際開發中如何避免死鎖

    • 設置超時時間
    • 多使用併發類而不是自己設計鎖
    • 盡量降低鎖的使用粒度:用不同的鎖而不是一個鎖,鎖的範圍越小越好
    • 避免鎖的嵌套:MustDeadLock類
    • 分配資源前先看能不能收回來:銀行家算法
    • 盡量不要幾個功能用同一把鎖:專鎖專用
    • 給你的線程起個有意義的名字:debug和排查時事半功倍,框架和JDK都遵守這個最佳實踐

    面試題9:什麼是活躍性問題?活鎖、飢餓和死鎖有什麼區別

    • 活鎖

      • 雖然線程並沒有阻塞,也始終在運行(所以叫做“活”鎖,線程是“活”的),但是程序卻得不到進展,因為線程始終互相謙讓,重複做同樣的事

      • 工程中的活鎖實例:消息隊列,消息如果處理失敗,就放在隊列開頭重試,沒阻塞程序無法繼續

      • 如何解決活鎖問題

        • 加入隨機因素,以太網的指數退避算法
    • 飢餓

      • 當線程需要某些資源(例如CPU),但是卻始終得不到,可能原因是飢餓線程的優先級過低

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 【K8S】Service服務詳解,看這一篇就夠了!!

    k8s用命名空間namespace把資源進行隔離,默認情況下,相同的命名空間里的服務可以相互通訊,反之進行隔離。

    1.1 Service

    Kubernetes中一個應用服務會有一個或多個實例(Pod,Pod可以通過rs進行多複本的建立),每個實例(Pod)的IP地址由網絡插件動態隨機分配(Pod重啟后IP地址會改變)。為屏蔽這些後端實例的動態變化和對多實例的負載均衡,引入了Service這個資源對象,如下所示:

    apiVersion: v1
    kind: Service
    metadata:
      name: nginx-svc
      labels:
        app: nginx
    spec:
      type: ClusterIP
      ports:
        - port: 80
           targetPort: 80
      selector:  #service通過selector和pod建立關聯
        app: nginx

    根據創建Service的type類型不同,可分成4種模式:

    • ClusterIP: 默認方式。根據是否生成ClusterIP又可分為普通Service和Headless Service兩類:
      • 普通Service:通過為Kubernetes的Service分配一個集群內部可訪問的固定虛擬IP(Cluster IP),實現集群內的訪問。為最常見的方式。
      • Headless Service:該服務不會分配Cluster IP,也不通過kube-proxy做反向代理和負載均衡。而是通過DNS提供穩定的絡ID來訪問,DNS會將headless service的後端直接解析為podIP列表。主要供StatefulSet使用。
    • NodePort:除了使用Cluster IP之外,還通過將service的port映射到集群內每個節點的相同一個端口,實現通過nodeIP:nodePort從集群外訪問服務。
    • LoadBalancer:和nodePort類似,不過除了使用一個Cluster IP和nodePort之外,還會向所使用的公有雲申請一個負載均衡器(負載均衡器後端映射到各節點的nodePort),實現從集群外通過LB訪問服務。
    • ExternalName:是 Service 的特例。此模式主要面向運行在集群外部的服務,通過它可以將外部服務映射進k8s集群,且具備k8s內服務的一些特徵(如具備namespace等屬性),來為集群內部提供服務。此模式要求kube-dns的版本為1.7或以上。這種模式和前三種模式(除headless service)最大的不同是重定向依賴的是dns層次,而不是通過kube-proxy。
      比如,在service定義中指定externalName的值”my.database.example.com”:

    此時k8s集群內的DNS服務會給集群內的服務名 ..svc.cluster.local 創建一個CNAME記錄,其值為指定的”my.database.example.com”。
    當查詢k8s集群內的服務my-service.prod.svc.cluster.local時,集群的 DNS 服務將返回映射的CNAME記錄”foo.bar.example.com”。

    備註: 前3種模式,定義服務的時候通過selector指定服務對應的pods,根據pods的地址創建出endpoints作為服務後端;Endpoints Controller會watch Service以及pod的變化,維護對應的Endpoint信息。kube-proxy根據Service和Endpoint來維護本地的路由規則。當Endpoint發生變化,即Service以及關聯的pod發生變化,kube-proxy都會在每個節點上更新iptables,實現一層負載均衡。 而ExternalName模式則不指定selector,相應的也就沒有port和endpoints。 ExternalName和ClusterIP中的Headles Service同屬於Headless Service的兩種情況。Headless Service主要是指不分配Service IP,且不通過kube-proxy做反向代理和負載均衡的服務。

    1.2 Port

    Service中主要涉及三種Port: * port 這裏的port表示service暴露在clusterIP上的端口,clusterIP:Port 是提供給集群內部訪問kubernetes服務的入口。

    • targetPort

    containerPort,targetPort是pod上的端口,從port和nodePort上到來的數據最終經過kube-proxy流入到後端pod的targetPort上進入容器。

    • nodePort

    nodeIP:nodePort 是提供給從集群外部訪問kubernetes服務的入口。

    總的來說,port和nodePort都是service的端口,前者暴露給從集群內訪問服務,後者暴露給從集群外訪問服務。從這兩個端口到來的數據都需要經過反向代理kube-proxy流入後端具體pod的targetPort,從而進入到pod上的容器內。

    1.3 IP

    使用Service服務還會涉及到幾種IP:

    • ClusterIP

    Pod IP 地址是實際存在於某個網卡(可以是虛擬設備)上的,但clusterIP就不一樣了,沒有網絡設備承載這個地址。它是一個虛擬地址,由kube-proxy使用iptables規則重新定向到其本地端口,再均衡到後端Pod。當kube-proxy發現一個新的service后,它會在本地節點打開一個任意端口,創建相應的iptables規則,重定向服務的clusterIP和port到這個新建的端口,開始接受到達這個服務的連接。

    • Pod IP

    Pod的IP,每個Pod啟動時,會自動創建一個鏡像為gcr.io/google_containers/pause的容器,Pod內部其他容器的網絡模式使用container模式,並指定為pause容器的ID,即:network_mode: “container:pause容器ID”,使得Pod內所有容器共享pause容器的網絡,與外部的通信經由此容器代理,pause容器的IP也可以稱為Pod IP。

    • 節點IP

    Node-IP,service對象在Cluster IP range池中分配到的IP只能在內部訪問,如果服務作為一個應用程序內部的層次,還是很合適的。如果這個service作為前端服務,準備為集群外的客戶提供業務,我們就需要給這個服務提供公共IP了。指定service的spec.type=NodePort,這個類型的service,系統會給它在集群的各個代理節點上分配一個節點級別的端口,能訪問到代理節點的客戶端都能訪問這個端口,從而訪問到服務。

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • 龍芯團隊完成CoreCLR MIPS64移植,已在github開源

    龍芯團隊完成CoreCLR MIPS64移植,已在github開源

    國產龍芯的軟件生態之中.NET不會缺席,畢竟 C# 與 .NetCore/Mono 也是全球幾大主流的編程語言和運行平台之一,最近一段時間聽到太多的鼓吹政務領域不支持.NET, 大家都明白這是某些人為了自己的利益打壓使用.NET技術的公司,我今天寫這篇文章就是想通過龍芯團隊的行動告訴更多人一起來推動.NET技術在中國的發展。希望龍芯廠商、支持龍芯的國產操作系統廠商能高度重視這個問題,主動加入 .Net Core 社區,加入.NET基金會,积極貢獻代碼,儘快做好適配工作。

    龍芯團隊一直在做net core的mips64移植工作,2020年6月18日完成了里程碑性的工作,在.NET Core 3.1分支上完成了MIPS64 的移植工作,目前已經在github上開源,開源地址:https://github.com/gsvm/coreclr 。具體說明可以參見 https://github.com/dotnet/runtime/issues/38069。 龍芯團隊正在做移植后的測試工作,已經完成了 9500 多項測試,ASP.NET Core示例程序 FlightFinder 已經可以在MIPS64 上正常運行,具體可以參看 https://github.com/dotnet/runtime/issues/4234。

    龍芯團隊還在github上面為龍芯.NET 建立了一個倉庫 https://github.com/gsvm/loongson-dotnet,用於關於龍芯的.NET信息,工作和下載,開源協議採用和.NET Core一樣的MIT協議。 根據這個倉庫的信息,龍芯團隊將在不久的將來發布.NET Core 3.1版本,然後升級到https://github.com/dotnet/runtime ,也就是.NET 5了。目前這項工作正在緊鑼密鼓的進行,非常歡迎大家的積极參与貢獻,包括issue或者PR,如果您有任何問題或需要任何支持,請隨時提交問題或通過电子郵件:aoqi@loongson.cn 與龍芯團隊聯繫。

    在文章的最後,我向你分享一個龍芯團隊成員 xiangzhai 在這個 https://github.com/xiangzhai/mono/issues/2 提到了指令集相關的編程的一些相關知識:

    OpenJDK、CorelCLR、mono都太大了,比較小的虛擬機例子可以看看PSP模擬器: https://github.com/xiangzhai/ppsspp-jit-mips64/commits/mips64-port-dev

    CoreCLR官方的文檔不錯:下降、寄存器分配、代碼生成 https://github.com/dotnet/runtime/blob/master/docs/design/coreclr/jit/ryujit-overview.md

    CoreCLR代碼生成常用調試方法: dotnet/runtime#606

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

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

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

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計最專業,超強功能平台可客製化

  • 如何提升自己?

    如何提升自己?

    如何提升自己?

    看完後浪,感慨良多…

    在程序員圈子,聽得最多的便是”35歲中年危機“。

    危機

    其實不僅僅存在“35歲危機”,還有“畢業危機”,“被裁員危機”,不僅僅在程序員圈子,幾乎所有圈子都是這樣,就像剛畢業的大學生說的:畢業等於失業。現在的社會飛速發展,我們常常感嘆大多數父母一代的人,智能手機玩着都比較費勁,其實也算是一種危機。其實不管任何職業,任何年齡的人,都應該保持“學習”的狀態,只有自身有了底氣,才能挺直了腰板,度過一個又一個危機。恩,做的不開心,我就換個工作…厲害的人,都是別人來請他去上班的。

    作為一個Javaer,當然也需要不斷的保持學習,特別是對於剛畢業的人,可能在找第一份工作的時候,你大廠與你擦肩而過,但是只要你對未來有一個完整的規劃,3年後,你一樣能達到你的目標。

    說了這麼多,只是為了強調學習的重要性。但是如何學習?學習什麼?這才是真正的問題。

    如何學習?

    很多人喜歡看視頻學習,記得剛學Java的時候,很多同學都會去看馬士兵,傳智博客等等。。。的確,視頻適合帶你入門,但是卻不適合進階。

    如果你是一個什麼都不知道的小白, 不知道什麼是IDE,是什麼叫配置環境變量,那麼的確可以看看視頻學習,因為它能帶你很快的上手,避免走很多坑。

    但是如果你是一個有一點項目經驗的人,那麼個人是不推薦通過視頻來學習新的知識的。第一個便是因為資源太少。除了培訓機構和各種專門為了做教育行業的人,很少有其他人會專門通過視頻介紹技術,即使有,視頻質量也難以得到保障。第二個便是效率問題,看視頻你不敢跳過,害怕錯過知識點,你也更加容易走神,因為進度掌握在別人手裡。

    所以最好的學習方式便是看資料,比如看書,看官方文檔等。

    如何看書?

    書讀百遍,其義自見。能真正把一本書看很多遍的人,一定能體會到這句話的精髓所在。

    擁有不同知識的人,看一本書的收貨一定是不一樣的。這裏可以簡單舉一個例子:一個剛學完Java基礎知識的人,看《Effective Java》的時候,可能只會死記硬背。一個擁有三年開發經驗的人,看《Effectice Java》的時候,會覺得恍然大悟。而一個擁有豐富的開發經驗的人,看《Effective Java》的時候,一定會激動的拍打着桌子:“對,當時這個坑就是這樣的”。

    當你想要了解一個知識點的時候,比如JVM,你可以先去各個網站,找一找網友推薦的書,一般比較經典的技術,都會有一本比較經典的書。比如JVM中的《深入理解Java虛擬機》。記住,如果是想深入了解的話,一定要買好書,湊字數的書,只適合你看個大概。

    挑選好一本書後,首先應該查看書的前言,然後看看目錄,了解整本書得框架以及知識點的分佈。最好帶着問題去看書。比如你看JVM,可能就是想了解大家常說的GC,JVM內存分佈,JVM調優等等,明白這些問題在書的第幾節,想想作者為什麼要把這個問題安排在這個地方?想要解答這些問題,需要明白哪些前提條件?

    做完上面的步驟后,就可以開始看書了,看一個好書,我建議一遍泛讀,兩遍精讀,三遍薄讀。

    第一遍,快速閱覽這本書,但是每個地方都要留一個印象,有問題不用怕,記在心裏。明白書的大體講了什麼,側重講了什麼,哪些是重點。更加重要的是,你在快速閱覽過程中,產生了什麼問題。

    當看完第一遍后,我不太建議立即去看第二遍,看完第一遍,應該對整個技術有個大概的了解,這個時候你應該實際去上手去做,比如JVM打打日誌看看,jps.exe,jstat.exe等調試工具用一用看看,嘗試將書中的內容應用到實際中。這個時候,你會產生更多的問題。

    第二遍,當經過一定的上手后,這個時候你就可以去看第二遍了,看第二遍的時候,心裏應該明白,你想解決什麼問題,你應該重點看哪裡。看的過程中,多想一想為什麼?想不明白的,一定上網查一查,問一問。這個過程中,其實更加推薦的是寫讀書筆記或者博客。嘗試將自己學到的東西講給別人聽,你會有意想不到的收穫。

    當看完第二遍后,就可以暫時休息了,因為一本書,寫的再好,看兩遍都會有點乏味,看完這遍后,整理下知識點,簡單回顧下。

    第三遍,第三遍應該在時間過去比較久之後再看,這一邊的速度可以很快,主要目的就是檢查你對這本書的內容的記憶程度理解的再好,都有可能會忘。每看到一部分內容,就去回想一下這部分內容的重點是什麼?有什麼值得注意的?為什麼是這樣。當你發現你都能說出來時,這本就,就已經薄成一張紙了、

    看哪些書?

    明白了怎麼看書之後,最後一個問題便是看哪些書了…

    作為一個程序員,最重要的便是基礎。基礎不牢,地動山搖。技術的迭代是非常快的,前幾年大火的C#,如今在國內需求已經比較少了,再比如現在慢慢崛起的go,想要不被時代拋棄,必須學會快速掌握一個新的知識,而這些知識其實都萬變不離其中。那便是基礎。

    掌握操作系統,能讓你快速明白JVM,多線程,NIO,零拷貝原理等。

    掌握網絡基礎,能讓你快速明白HttpSocketHttps

    當然,這裏所說的基礎,也包括一本語言的基礎,比如Java開發基礎等。

    等有了這些基礎知識,再去學習整體開發的框架,會事半功倍。

    明白了基礎的重要性,剩下的就是掌握一個高級開發工程師應該掌握的技能。

    然而,什麼才是一個高級開發工程師應該掌握的技能?

    很遺憾,我不能告訴你。因為不同方向,不同企業,不同部門,不同的業務。對一個人技能的要求,是不一樣的。

    最好的方法便是定製一個目標,然後看看你離這個目標還有多遠。

    怎麼去衡量你離這個目標還有多遠呢?最好的答案便是面試。面試犹如考試,少看哪些博眼球的文章標題為面試官問我…,製造焦慮,太偏的知識點可以簡單了解,但是別太浪費時間。不管你有沒有準備好,現在開始,準備一份你的簡歷,找一些差不多的崗位,然後接受面試官的鞭撻。總結每一次面試中,你發現你有空白的知識點,然後找一本書,看它。不用害怕簡歷沒什麼寫的,沒什麼寫的簡歷,更應該開始着手準備,機會總是給有準備的人。

    堅持上面我說的,我相信,offer會比“危機”先到一步。

    有感而發,隨便寫寫。

    —— 胖毛2020/06/19

    個人公眾號,隨便寫寫

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • C#中的閉包和意想不到的坑

    C#中的閉包和意想不到的坑

    雖然閉包主要是函數式編程的玩意兒,而C#的最主要特徵是面向對象,但是利用委託或lambda表達式,C#也可以寫出具有函數式編程風味的代碼。同樣的,使用委託或者lambda表達式,也可以在C#中使用閉包。

    根據WIKI的定義,閉包又稱語法閉包或函數閉包,是在函數式編程語言中實現語法綁定的一種技術。閉包在實現上是一個結構體,它存儲了一個函數(通常是其入口地址)和一個關聯的環境(相當於一個符號查找表)。閉包也可以延遲變量的生存周期。

    嗯。。看定義好像有點迷糊,讓我們看看下面的例子吧

        class Program
        {
            static Action CreateGreeting(string message)
            {
                return () => { Console.WriteLine("Hello " + message); };
            }
    
            static void Main()
            {
                Action action = CreateGreeting("DeathArthas");
                action();
            }
        }
    

    這個例子非常簡單,用lambda表達式創建一個Action對象,之後再調用這個Action對象。
    但是仔細觀察會發現,當Action對象被調用的時候,CreateGreeting方法已經返回了,作為它的實參的message應該已經被銷毀了,那麼為什麼我們在調用Action對象的時候,還是能夠得到正確的結果呢?
     
    原來奧秘就在於,這裏形成了閉包。雖然CreateGreeting已經返回了,但是它的局部變量被返回的lambda表達式所捕獲,延遲了其生命周期。怎麼樣,這樣再回頭看閉包定義,是不是更清楚了一些?
     
    閉包就是這麼簡單,其實我們經常都在使用,只是有時候我們都不自知而已。比如大家肯定都寫過類似下面的代碼。

    void AddControlClickLogger(Control control, string message)
    {
    	control.Click += delegate
    	{
    		Console.WriteLine("Control clicked: {0}", message);
    	}
    }
    

    這裏的代碼其實就用了閉包,因為我們可以肯定,在control被點擊的時候,這個message早就超過了它的聲明周期。合理使用閉包,可以確保我們寫出在空間和時間上面解耦的委託。
     
    不過在使用閉包的時候,要注意一個陷阱。因為閉包會延遲局部變量的生命周期,在某些情況下程序產生的結果會和預想的不一樣。讓我們看看下面的例子。

        class Program
        {
    	static List<Action> CreateActions()
            {
                var result = new List<Action>();
                for(int i = 0; i < 5; i++)
                {
                    result.Add(() => Console.WriteLine(i));
                }
                return result;
            }
    
            static void Main()
            {
                var actions = CreateActions();
                for(int i = 0;i<actions.Count;i++)
                {
                    actions[i]();
                }
            }
        }
    

    這個例子也非常簡單,創建一個Action鏈表並依次執行它們。看看結果

    相信很多人看到這個結果的表情是這樣的!!難道不應該是0,1,2,3,4嗎?出了什麼問題?

    刨根問底,這兒的問題還是出現在閉包的本質上面,作為“閉包延遲了變量的生命周期”這個硬幣的另外一面,是一個變量可能在不經意間被多個閉包所引用。

    在這個例子裏面,局部變量i同時被5個閉包引用,這5個閉包共享i,所以最後他們打印出來的值是一樣的,都是i最後退出循環時候的值5。

    要想解決這個問題也很簡單,多聲明一個局部變量,讓各個閉包引用自己的局部變量就可以了。

    	//其他都保持與之前一致
            static List<Action> CreateActions()
            {
                var result = new List<Action>();
                for (int i = 0; i < 5; i++)
                {
                    int temp = i; //添加局部變量
                    result.Add(() => Console.WriteLine(temp));
                }
                return result;
            }
    

    這樣各個閉包引用不同的局部變量,剛剛的問題就解決了。

    除此之外,還有一個修復的方法,在創建閉包的時候,使用foreach而不是for。至少在C# 7.0 的版本上面,這個問題已經被注意到了,使用foreach的時候編譯器會自動生成代碼繞過這個閉包陷阱。

    	//這樣fix也是可以的
            static List<Action> CreateActions()
            {
                var result = new List<Action>();
                foreach (var i in Enumerable.Range(0,5))
                {
                    result.Add(() => Console.WriteLine(i));
                }
                return result;
            }
    

    這就是在閉包在C#中的使用和其使用中的一個小陷阱,希望大家能通過老胡的文章了解到這個知識點並且在開發中少走彎路!

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • Thunk函數的使用

    Thunk函數的使用

    編譯器的求值策略通常分為傳值調用以及傳名調用,Thunk函數是應用於編譯器的傳名調用實現,往往是將參數放到一個臨時函數之中,再將這個臨時函數傳入函數體,這個臨時函數就叫做Thunk 函數。

    求值策略

    編譯器的求值策略通常分為傳值調用以及傳名調用,在下面的例子中,將一個表達式作為參數進行傳遞,傳值調用以及傳名調用中實現的方式有所不同。

    var x = 1;
    
    function s(y){
        console.log(y + 1); // 3
    }
    
    s(x + 1);
    

    在上述的例子中,無論是使用傳值調用還是使用傳名調用,執行的結果都是一樣的,但是其調用過程不同:

    • 傳值調用:首先計算x + 1,然後將計算結果2傳遞到s函數,即相當於調用s(2)
    • 傳名調用:直接將x + 1表達式傳遞給y,使用時再計算x + 1,即相當於計算(x + 1) + 1

    傳值調用與傳名調用各有利弊,傳值調用比較簡單,但是對參數求值的時候,實際上還沒用到這個參數,有可能造成沒有必要的計算。傳名調用可以解決這個問題,但是實現相對來說比較複雜。

    var x = 1;
    
    function s(y){
        console.log(y + 1); // 3
    }
    
    s(x + 1, x + 2);
    

    在上面這個例子中,函數s並沒有用到x + 2這個表達式求得的值,使用傳名調用的話只將表達式傳入而並未計算,只要在函數中沒有用到x + 2這個表達式就不會計算,使用傳值調用的話就會首先將x + 2的值計算然後傳入,如果沒有用到這個值,那麼就多了一次沒有必要的計算。Thunk函數就是作為傳名調用的實現而構建的,往往是將參數放到一個臨時函數之中,再將這個臨時函數傳入函數體,這個臨時函數就叫做Thunk 函數。

    var x = 1;
    
    function s(y){
        console.log(y + 1); // 3
    }
    
    s(x + 1);
    
    // 等同於
    
    var x = 1;
    
    function s(thunk){
        console.log(thunk() + 1); // 3
    }
    
    var thunk = function(){
        return x + 1;
    }
    
    s(thunk);
    

    Js中的Thunk函數

    Js中的求值策略是是傳值調用,在Js中使用Thunk函數需要手動進行實現且含義有所不同,在Js中,Thunk函數替換的不是表達式,而是多參數函數,將其替換成單參數的版本,且只接受回調函數作為參數。

    // 假設一個延時函數需要傳遞一些參數
    // 通常使用的版本如下
    var delayAsync = function(time, callback, ...args){
        setTimeout(() => callback(...args), time);
    }
    
    var callback = function(x, y, z){
        console.log(x, y, z);
    }
    
    delayAsync(1000, callback, 1, 2, 3);
    
    // 使用Thunk函數
    
    var thunk = function(time, ...args){
        return function(callback){
            setTimeout(() => callback(...args), time);
        }
    }
    
    var callback = function(x, y, z){
        console.log(x, y, z);
    }
    
    var delayAsyncThunk = thunk(1000, 1, 2, 3);
    delayAsyncThunk(callback);
    

    實現一個簡單的Thunk函數轉換器,對於任何函數,只要參數有回調函數,就能寫成Thunk函數的形式。

    var convertToThunk = function(funct){
      return function (...args){
        return function (callback){
          return funct.apply(this, args);
        }
      };
    };
    
    var callback = function(x, y, z){
        console.log(x, y, z);
    }
    
    var delayAsyncThunk = convertToThunk(function(time, ...args){
        setTimeout(() => callback(...args), time);
    });
    
    thunkFunct = delayAsyncThunk(1000, 1, 2, 3);
    thunkFunct(callback);
    

    Thunk函數在ES6之前可能應用比較少,但是在ES6之後,出現了Generator函數,通過使用Thunk函數就可以可以用於Generator函數的自動流程管理。首先是關於Generator函數的基本使用,調用一個生成器函數並不會馬上執行它裏面的語句,而是返回一個這個生成器的迭代器iterator 對象,他是一個指向內部狀態對象的指針。當這個迭代器的next()方法被首次(後續)調用時,其內的語句會執行到第一個(後續)出現yield的位置為止,yield后緊跟迭代器要返回的值,也就是指針就會從函數頭部或者上一次停下來的地方開始執行到下一個yield。或者如果用的是yield*,則表示將執行權移交給另一個生成器函數(當前生成器暫停執行)。

    function* f(x) {
        yield x + 10;
        yield x + 20;
        return x + 30;
    }
    var g = f(1);
    console.log(g); // f {<suspended>}
    console.log(g.next()); // {value: 11, done: false}
    console.log(g.next()); // {value: 21, done: false}
    console.log(g.next()); // {value: 31, done: true}
    console.log(g.next()); // {value: undefined, done: true} // 可以無限next(),但是value總為undefined,done總為true
    

    由於Generator函數能夠將函數的執行暫時掛起,那麼他就完全可以操作一個異步任務,當上一個任務完成之後再繼續下一個任務,下面這個例子就是將一個異步任務同步化表達,當上一個延時定時器完成之後才會進行下一個定時器任務,可以通過這種方式解決一個異步嵌套的問題,例如利用回調的方式需要在一個網絡請求之後加入一次回調進行下一次請求,很容易造成回調地獄,而通過Generator函數就可以解決這個問題,事實上async/await就是利用的Generator函數以及Promise實現的異步解決方案。

    var it = null;
    
    function f(){
        var rand = Math.random() * 2;
        setTimeout(function(){
            if(it) it.next(rand);
        },1000)
    }
    
    function* g(){ 
        var r1 = yield f();
        console.log(r1);
        var r2 = yield f();
        console.log(r2);
        var r3 = yield f();
        console.log(r3);
    }
    
    it = g();
    it.next();
    

    雖然上邊的例子能夠自動執行,但是不夠方便,現在實現一個Thunk函數的自動流程管理,其自動幫我們進行回調函數的處理,只需要在Thunk函數中傳遞一些函數執行所需要的參數比如例子中的index,然後就可以編寫Generator函數的函數體,通過左邊的變量接收Thunk函數中funct執行的參數,在使用Thunk函數進行自動流程管理時,必須保證yield后是一個Thunk函數。
    關於自動流程管理run函數,首先需要知道在調用next()方法時,如果傳入了參數,那麼這個參數會傳給上一條執行的yield語句左邊的變量,在這個函數中,第一次執行next時並未傳遞參數,而且在第一個yield上邊也並不存在接收變量的語句,無需傳遞參數,接下來就是判斷是否執行完這個生成器函數,在這裏並沒有執行完,那麼將自定義的next函數傳入res.value中,這裏需要注意res.value是一個函數,可以在下邊的例子中將註釋的那一行執行,然後就可以看到這個值是f(funct){...},此時我們將自定義的next函數傳遞后,就將next的執行權限交予了f這個函數,在這個函數執行完異步任務后,會執行回調函數,在這個回調函數中會觸發生成器的下一個next方法,並且這個next方法是傳遞了參數的,上文提到傳入參數後會將其傳遞給上一條執行的yield語句左邊的變量,那麼在這一次執行中會將這個參數值傳遞給r1,然後在繼續執行next,不斷往複,直到生成器函數結束運行,這樣就實現了流程的自動管理。

    function thunkFunct(index){
        return function f(funct){
            var rand = Math.random() * 2;
            setTimeout(() => funct({rand:rand, index: index}), 1000)
        }
    }
    
    function* g(){ 
        var r1 = yield thunkFunct(1);
        console.log(r1.index, r1.rand);
        var r2 = yield thunkFunct(2);
        console.log(r2.index, r2.rand);
        var r3 = yield thunkFunct(3);
        console.log(r3.index, r3.rand);
    }
    
    function run(generator){
        var g = generator();
    
        var next = function(data){
            var res = g.next(data);
            if(res.done) return ;
            // console.log(res.value);
            res.value(next);
        }
    
        next();
    }
    
    run(g);
    

    每日一題

    https://github.com/WindrunnerMax/EveryDay
    

    參考

    https://www.jianshu.com/p/9302a1d01113
    https://segmentfault.com/a/1190000017211798
    http://www.ruanyifeng.com/blog/2015/05/thunk.html
    

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

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

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

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計最專業,超強功能平台可客製化

  • Linux MySQL分庫分表之Mycat

    Linux MySQL分庫分表之Mycat

    介紹

    背景

    • 表的個數達到了幾百千萬張表時,眾多的業務模塊都訪問這個數據庫,壓力會比較大,考慮對其進行分庫
    • 表的數據達到幾千萬級別,在做很多操作都比較吃力,考慮對其進行分庫或分表

    數據切分(sharding)方案

      數據的切分(Sharding)根據其切分規則的類型,可以分為兩種切分模式:

    • 垂直切分:按照業務模塊進行切分,將不同模塊的表切分到不同的數據庫中
    • 水平切分,將一張大表按照一定的切分規則,按照行切分成不同的表或者切分到不同的庫中

    如何理解垂直切分?

      垂直分庫:主要解決的問題是單個數據庫中[數據表]過多問題

      垂直分表:主要解決的問題是單個中[過多問題(將一張大表,拆分不同的關聯表)。

    如何理解水平切分?

      水平切分主要解決的問題就是對於[單表數據量過大]的問題(1000W以上數據性能會有所下降)

    切分原則

    1. 能不切盡量不要切分
    2. 如果要切分一定要選擇合適的切分規則,提前規劃好
    3. 數據切分盡量通過冗餘或表分組(Table Group)來降低跨庫Join的可能
    4. 由於數據庫中間件對數據Join實現的優劣難以把握,而且實現高性能難度極大,業務讀取盡量少使用多表Join

    分庫分表之後帶來問題?

    1. 跨庫Join:訂單表需要關聯會員信息(訂單表和會員表拆分為兩個庫的表)
      1. 應用層由一個查詢拆分為多個
      2. 全局表,每個庫都存儲相同的數據,比如字典表、地址表
      3. 字段冗餘
      4. Mycat技術可以實現跨庫Join,只能實現2張表跨庫Join
    2. 分佈式事務(Mycat沒有很好實現分佈式事務)
      1. 強一致性(互聯網項目不推薦,性能不好)
      2. 最終一致性(異步方式去實現,需要通過日誌信息)
    3. 主鍵問題(保證ID的連續性和唯一性)
      1. UUID(性能不好)
      2. redis incr命令
      3. zookeeper
      4. 雪花算法
    4. 跨庫進行排序問題
      1. 在應用層進行排序

    Mycat應用

    官網鏈接

    點我直達

    Mycat核心概念

    • Schema:由它制定邏輯數據庫(相當於MySQL的database數據庫)
    • Table:邏輯表(相當於MySQL的table表)
    • DataNode:真正存儲數據的物理節點
    • DataHost:存儲節點所在的數據庫主機(指定MySQL數據庫的連接信息)
    • User:MyCat的用戶(類似於MySQL的用戶,支持多用戶)

    MyCat主要解決的問題

    • 海量數據存儲
    • 查詢優化

    Mycat對數據庫的支持

    Mycat安裝

    安裝要求

    • jdk:要求jdk必須是1.7及以上版本 (我使用的是jdk 1.8

    • Mysql:推薦mysql是5.5以上版本(我使用的是mysql 5.7

    安裝jdk

    具體教程:點我直達

    Mcat下載

    下載鏈接:點我直達

    百度雲盤地址:https://pan.baidu.com/s/14A3BAwnBRGZppc3AicF5Hw  密碼: gkrp

    解壓

    修改配置文件

    路徑:/cyb/soft/mycat/conf

    server.xml

    用途:用於配置用戶信息

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
        - you may not use this file except in compliance with the License. - You 
        may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
        - - Unless required by applicable law or agreed to in writing, software - 
        distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
        WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
        License for the specific language governing permissions and - limitations 
        under the License. -->
    <!DOCTYPE mycat:server SYSTEM "server.dtd">
    <mycat:server xmlns:mycat="http://io.mycat/">
        <system>
        <property name="useSqlStat">0</property>  <!-- 1為開啟實時統計、0為關閉 -->
        <property name="useGlobleTableCheck">0</property>  <!-- 1為開啟全加班一致性檢測、0為關閉 -->
    
            <property name="sequnceHandlerType">2</property>
          <!--  <property name="useCompression">1</property>--> <!--1為開啟mysql壓縮協議-->
            <!--  <property name="fakeMySQLVersion">5.6.20</property>--> <!--設置模擬的MySQL版本號-->
        <!-- <property name="processorBufferChunk">40960</property> -->
        <!-- 
        <property name="processors">1</property> 
        <property name="processorExecutor">32</property> 
         -->
            <!--默認為type 0: DirectByteBufferPool | type 1 ByteBufferArena-->
            <property name="processorBufferPoolType">0</property>
            <!--默認是65535 64K 用於sql解析時最大文本長度 -->
            <!--<property name="maxStringLiteralLength">65535</property>-->
            <!--<property name="sequnceHandlerType">0</property>-->
            <!--<property name="backSocketNoDelay">1</property>-->
            <!--<property name="frontSocketNoDelay">1</property>-->
            <!--<property name="processorExecutor">16</property>-->
            <!--
                <property name="serverPort">8066</property> <property name="managerPort">9066</property> 
                <property name="idleTimeout">300000</property> <property name="bindIp">0.0.0.0</property> 
                <property name="frontWriteQueueSize">4096</property> <property name="processors">32</property> -->
            <!--分佈式事務開關,0為不過濾分佈式事務,1為過濾分佈式事務(如果分佈式事務內只涉及全局表,則不過濾),2為不過濾分佈式事務,但是記錄分佈式事務日誌-->
            <property name="handleDistributedTransactions">0</property>
            
                <!--
                off heap for merge/order/group/limit      1開啟   0關閉
            -->
            <property name="useOffHeapForMerge">1</property>
    
            <!--
                單位為m
            -->
            <property name="memoryPageSize">1m</property>
    
            <!--
                單位為k
            -->
            <property name="spillsFileBufferSize">1k</property>
    
            <property name="useStreamOutput">0</property>
    
            <!--
                單位為m
            -->
            <property name="systemReserveMemorySize">384m</property>
    
    
            <!--是否採用zookeeper協調切換  -->
            <property name="useZKSwitch">true</property>
    
    
        </system>
        
        <!-- 全局SQL防火牆設置 -->
        <!-- 
        <firewall> 
           <whitehost>
              <host host="127.0.0.1" user="mycat"/>
              <host host="127.0.0.2" user="mycat"/>
           </whitehost>
           <blacklist check="false">
           </blacklist>
        </firewall>
        -->
        
        <user name="root">
            <property name="password">root</property>
            <property name="schemas">TESTDB</property>
            
            <!-- 表級 DML 權限設置 -->
            <!--         
            <privileges check="false">
                <schema name="TESTDB" dml="0110" >
                    <table name="tb01" dml="0000"></table>
                    <table name="tb02" dml="1111"></table>
                </schema>
            </privileges>        
             -->
        </user>
    
        <user name="user">
            <property name="password">user</property>
            <property name="schemas">TESTDB</property>
            <property name="readOnly">true</property>
        </user>
    
    </mycat:server>

    schema.xml

    用途:管理邏輯表

    為了演示方便,刪掉一些不必要的標籤,標籤詳細用法:點我直達

    <?xml version="1.0"?>
    <!DOCTYPE mycat:schema SYSTEM "schema.dtd">
    <mycat:schema xmlns:mycat="http://io.mycat/">
    
        <schema name="TESTDB" checkSQLschema="true" sqlMaxLimit="100">
            <!-- auto sharding by id (long) -->
            <table name="cyb_test" dataNode="dn1,dn2,dn3" rule="mod-long" />
        </schema>
        <dataNode name="dn1" dataHost="localhost1" database="db1" />
        <dataNode name="dn2" dataHost="localhost1" database="db2" />
        <dataNode name="dn3" dataHost="localhost1" database="db3" />
        <dataHost name="localhost1" maxCon="1000" minCon="10" balance="0"
                  writeType="0" dbType="mysql" dbDriver="native" switchType="1"  slaveThreshold="100">
            <heartbeat>select user()</heartbeat>
            <!-- can have multi write hosts -->
            <writeHost host="hostM1" url="192.168.31.200:3306" user="root"
                       password="root">
                <!-- can have multi read hosts -->
                <readHost host="hostS2" url="192.168.31.201:3306" user="root" password="root" />
            </writeHost>
        </dataHost>
    </mycat:schema>

    rule.xml

    用途:定義了我們對錶進行拆分所涉及到的規則定義,視情況修改參數

    <?xml version="1.0" encoding="UTF-8"?>
    <!-- - - Licensed under the Apache License, Version 2.0 (the "License"); 
        - you may not use this file except in compliance with the License. - You 
        may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 
        - - Unless required by applicable law or agreed to in writing, software - 
        distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT 
        WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the 
        License for the specific language governing permissions and - limitations 
        under the License. -->
    <!DOCTYPE mycat:rule SYSTEM "rule.dtd">
    <mycat:rule xmlns:mycat="http://io.mycat/">
        <tableRule name="rule1">
            <rule>
                <columns>id</columns>
                <algorithm>func1</algorithm>
            </rule>
        </tableRule>
    
        <tableRule name="rule2">
            <rule>
                <columns>user_id</columns>
                <algorithm>func1</algorithm>
            </rule>
        </tableRule>
    
        <tableRule name="sharding-by-intfile">
            <rule>
                <columns>sharding_id</columns>
                <algorithm>hash-int</algorithm>
            </rule>
        </tableRule>
        <tableRule name="auto-sharding-long">
            <rule>
                <columns>id</columns>
                <algorithm>rang-long</algorithm>
            </rule>
        </tableRule>
        <tableRule name="mod-long">
            <rule>
                <columns>id</columns>
                <algorithm>mod-long</algorithm>
            </rule>
        </tableRule>
        <tableRule name="sharding-by-murmur">
            <rule>
                <columns>id</columns>
                <algorithm>murmur</algorithm>
            </rule>
        </tableRule>
        <tableRule name="crc32slot">
            <rule>
                <columns>id</columns>
                <algorithm>crc32slot</algorithm>
            </rule>
        </tableRule>
        <tableRule name="sharding-by-month">
            <rule>
                <columns>create_time</columns>
                <algorithm>partbymonth</algorithm>
            </rule>
        </tableRule>
        <tableRule name="latest-month-calldate">
            <rule>
                <columns>calldate</columns>
                <algorithm>latestMonth</algorithm>
            </rule>
        </tableRule>
        
        <tableRule name="auto-sharding-rang-mod">
            <rule>
                <columns>id</columns>
                <algorithm>rang-mod</algorithm>
            </rule>
        </tableRule>
        
        <tableRule name="jch">
            <rule>
                <columns>id</columns>
                <algorithm>jump-consistent-hash</algorithm>
            </rule>
        </tableRule>
    
        <function name="murmur"
            class="io.mycat.route.function.PartitionByMurmurHash">
            <property name="seed">0</property><!-- 默認是0 -->
            <property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片 -->
            <property name="virtualBucketTimes">160</property><!-- 一個實際的數據庫節點被映射為這麼多虛擬節點,默認是160倍,也就是虛擬節點數是物理節點數的160倍 -->
            <!-- <property name="weightMapFile">weightMapFile</property> 節點的權重,沒有指定權重的節點默認是1。以properties文件的格式填寫,以從0開始到count-1的整數值也就是節點索引為key,以節點權重值為值。所有權重值必須是正整數,否則以1代替 -->
            <!-- <property name="bucketMapPath">/etc/mycat/bucketMapPath</property> 
                用於測試時觀察各物理節點與虛擬節點的分佈情況,如果指定了這個屬性,會把虛擬節點的murmur hash值與物理節點的映射按行輸出到這個文件,沒有默認值,如果不指定,就不會輸出任何東西 -->
        </function>
    
        <function name="crc32slot"
                  class="io.mycat.route.function.PartitionByCRC32PreSlot">
            <property name="count">2</property><!-- 要分片的數據庫節點數量,必須指定,否則沒法分片 -->
        </function>
        <function name="hash-int"
            class="io.mycat.route.function.PartitionByFileMap">
            <property name="mapFile">partition-hash-int.txt</property>
        </function>
        <function name="rang-long"
            class="io.mycat.route.function.AutoPartitionByLong">
            <property name="mapFile">autopartition-long.txt</property>
        </function>
        <function name="mod-long" class="io.mycat.route.function.PartitionByMod">
            <!-- how many data nodes -->
            <property name="count">3</property>
        </function>
    
        <function name="func1" class="io.mycat.route.function.PartitionByLong">
            <property name="partitionCount">8</property>
            <property name="partitionLength">128</property>
        </function>
        <function name="latestMonth"
            class="io.mycat.route.function.LatestMonthPartion">
            <property name="splitOneDay">24</property>
        </function>
        <function name="partbymonth"
            class="io.mycat.route.function.PartitionByMonth">
            <property name="dateFormat">yyyy-MM-dd</property>
            <property name="sBeginDate">2015-01-01</property>
        </function>
        
        <function name="rang-mod" class="io.mycat.route.function.PartitionByRangeMod">
                <property name="mapFile">partition-range-mod.txt</property>
        </function>
        
        <function name="jump-consistent-hash" class="io.mycat.route.function.PartitionByJumpConsistentHash">
            <property name="totalBuckets">3</property>
        </function>
    </mycat:rule>

    啟動mycat

    進入mycat/bin,啟動mycat
    
    啟動命令:./mycat start
    停止命令:./mycat stop
    重啟命令:./mycat restart
    查看狀態命令:./mycat status

    注意,可以使用mysql的客戶端直接連接mycat服務,默認端口為8066

    錯誤日誌(重要)

      部署過程中,我碰到點小問題,找不到主機名,具體解決方案,請看我另一篇:點我直達 ,如果Mycat服務起不來,記得看錯誤日誌喲!

    測試

    ip:192.168.31.200(mysql主服務器)

    ip:192.168.31.201(mysql從服務器)

    ip:192.168.31.209(mycat服務器)

      注:演示過程中,因為mysql搭建了集群,主從複製,可能網絡原因,有些延遲,或者mysql主從複製同步機制問題,導致刷新好幾次,才显示出來,因為圖片較大,被分割幾張gif,內容都是連續的,驗證結果,達到預期,演示成功!

      MySQL集群搭建主從複製:點我直達

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

    ※台北網頁設計公司全省服務真心推薦

    ※想知道最厲害的網頁設計公司"嚨底家"!

    ※推薦評價好的iphone維修中心

  • 區塊鏈系列教程之:比特幣的錢包與交易

    區塊鏈系列教程之:比特幣的錢包與交易

    目錄

    • 簡介
    • 比特幣密碼學的基礎
      • 單向散列函數(hash算法)
      • 非對稱加密算法
      • 擴展閱讀:同態加密
    • 密鑰,地址和錢包
    • 比特幣中的交易
    • 擴展閱讀:圖靈非完備性
    • 總結

    簡介

    錢包在比特幣中是做什麼的呢?比特幣的交易又有什麼特點呢?怎麼才能偽造比特幣的交易呢?今天和大家一起學習一下比特幣中的錢包和交易。

    比特幣密碼學的基礎

    之前我們提到過比特幣使用的並不是什麼新技術,只是對於老的技術比如:P2P網絡,分佈式系統,密碼學,共識算法的重新而又巧妙的應用。

    在錢包和交易生成驗證的過程中,都需要使用到密碼學的計算。這裏我們先介紹一下比特幣中會使用到的幾種密碼學技術。

    更多精彩內容且看:

    • 區塊鏈從入門到放棄系列教程-涵蓋密碼學,超級賬本,以太坊,Libra,比特幣等持續更新
    • Spring Boot 2.X系列教程:七天從無到有掌握Spring Boot-持續更新
    • Spring 5.X系列教程:滿足你對Spring5的一切想象-持續更新
    • java程序員從小工到專家成神之路(2020版)-持續更新中,附詳細文章教程

    單向散列函數(hash算法)

    在介紹單向散列函數之前,我們先了解一下什麼情況下需要使用到單向散列函數。

    如果你需要從國外的網站上下載一個軟件,但是因為種種原因,國外的網絡太慢了,下載幾個G的數據幾乎是不可能的。剛好國內有鏡像網站,可以從國內下載數據。但是如何保證國內的鏡像不是被篡改過後的呢?這個時候就需要單向散列函數了。一般來說網站會提供MD5或者SHA的值作為驗證值。

    單向散列函數有一個輸入和輸出。輸入稱為消息,輸出稱為散列值。

    散列值的長度跟消息的長度無關,不論多少大小的長度的消息,都會計算出固定長度的散列值。

    hash算法有下面幾個特點:

    1. 能夠根據任意長度的消息計算出固定長度的散列值。

    2. 計算速度要快。

    3. 消息不同,散列值也不同。

      這就意味着,如果僅僅是一點點的變動都會引起整個散列值的巨大變化。

      因為散列值的大小是固定的,所以有可能會出現不同的消息產生相同散列值的情況。這種情況叫做碰撞。

      難以發現碰撞的性質被稱為抗碰撞性。當給定某條消息的散列值時,必須保證很難找到和該消息具有相同散列值的另一條消息。

    4. 單向散列函數必須具有單向性。所謂單向性是指無法通過散列值來反推出消息的性質。

    比特幣使用的散列算法是SHA256,他是安全散列算法SHA(Secure Hash Algorithm)系列算法的一種(另外還有SHA-1、SHA-224、SHA-384 和 SHA-512 等變體),SHA是美國國家安全局 (NSA) 設計,美國國家標準與技術研究院(NIST) 發布的,主要適用於数字簽名標準(DigitalSignature Standard DSS)裏面定義的数字簽名算法(Digital Signature Algorithm DSA)。

    RIPEMD(RACE Integrity Primitives Evaluation Message Digest,RACE原始完整性校驗消息摘要),是Hans Dobbertin等3人在md4,md5的基礎上,於1996年提出來的。

    非對稱加密算法

    非對稱加密算法也叫公鑰密碼算法,通過生成的公私鑰來對明文密文進行加密解密。

    非對稱加密算法需要兩個密鑰:公開密鑰(publickey)和私有密鑰(privatekey)。公開密鑰與私有密鑰是一對,如果用公開密鑰對數據進行加密,只有用對應的私有密鑰才能解密;如果用私有密鑰對數據進行加密,那麼只有用對應的公開密鑰才能解密。因為加密和解密使用的是兩個不同的密鑰,所以這種算法叫作非對稱加密算法。

    擴展閱讀:同態加密

    同態加密是一種加密形式,它允許人們對密文進行特定的代數運算得到仍然是加密的結果,將其解密所得到的結果與對明文進行同樣的運算結果一樣。換言之,這項技術令人們可以在加密的數據中進行諸如檢索、比較等操作,得出正確的結果,而在整個處理過程中無需對數據進行解密。其意義在於,真正從根本上解決將數據及其操作委託給第三方時的保密問題,例如對於各種雲計算的應用。

    密鑰,地址和錢包

    比特幣的所有權是通過数字密鑰、比特幣地址和数字簽名來確立的。数字密鑰實際上並不是存儲在網絡中,而是由用戶生成並存儲在一個文件或簡單的數據庫 中,稱為錢包。存儲在用戶錢包中的数字密鑰完全獨立於比特幣協議,可由用戶的錢包軟件生成並管理,而無需區塊鏈或網絡連接。密鑰實現了比特幣的許多有趣特性,包括去中心化信任和控制、所有權認證和基於密碼學證明的安全模型。

    比特幣錢包只包含私鑰而不是比特幣。每一個用戶有一個包含多個私鑰的錢包。錢包中包含成對的私鑰和公鑰。用戶用這些私鑰來簽名交易,從而證明它們擁有交易的輸出(也就是其中的比特幣)。比特幣是以交易輸出的形式來儲存在區塊鏈中(通常記為vout或txout)。

    如果錢包只包含私鑰,那麼錢包地址是什麼呢?錢包地址是從公鑰的hash值的出來的,如下圖所示:

    1. 首先使用隨機數發生器生成一個『私鑰』。一般來說這是一個256bits的數,擁有了這串数字就可以對相應『錢包地址』中的比特幣進行操作,所以必須被安全地保存起來。

    2. 『私鑰』經過SECP256K1算法處理生成了『公鑰』。SECP256K1是一種橢圓曲線算法,通過一個已知『私鑰』時可以算得『公鑰』,而『公鑰』已知時卻無法反向計算出『私鑰』。這是保障比特幣安全的算法基礎。

    3. 同SHA256一樣,RIPEMD160也是一種Hash算法,由『公鑰』可以計算得到『公鑰哈希』,而反過來是行不通的。

    4. 將一個字節的地址版本號連接到『公鑰哈希』頭部(對於比特幣網絡的pubkey地址,這一字節為“0”),然後對其進行兩次SHA256運算,將結果的前4字節作為『公鑰哈希』的校驗值,連接在其尾部。

    5. 將上一步結果使用BASE58進行編碼(比特幣定製版本),就得到了『錢包地址』。 比如,1A1zP1eP5QGefi2DMPTfTL5TTmv7DivfNa。

    所以私鑰,公鑰和錢包地址的關係如下圖所示:

    大家看到錢包地址1A1zP1eP5QGefi2DMPTfTL5TTmv7DivfNa有什麼想法呢?

    肯定有人在想,這麼一大長串字母和数字實在是太不好記憶了。能不能生產一個比較好記的錢包地址呢? 比如MyNameIsHanMeiMei….這樣開頭的地址呢?

    當然可以,這叫做靚號地址,只不過需要大量的算力才行。

    比特幣中的交易

    簡單來說,交易就是告知全網:比特幣的持有者已授權把比特幣轉帳給其他人。而新持有者能夠再次授權,轉移給該比特幣所有權鏈中的其他人。

    注意, 在比特幣的世界里既沒有賬戶,也沒有餘額,只有分散到區塊鏈里的UTXO(Unspent Transaction Outputs)。

    怎麼理解這個UTXO呢?沒有賬戶也沒有餘額,那麼錢包裏面的金額是怎麼計算出來的呢?

    別急,讓我們一一道來。

    話說,在比特幣中,比特幣錢包間的轉賬是通過交易(Transaction)實現的。

    我們看一個標準的交易流程。

    那麼問題來了,世界上第一個比特幣是哪裡來的呢?

    答,是挖礦來的。好了,我們的001交易表示的就是一個挖礦的過程,在這個交易中,輸入就是挖礦,輸出編號1,BTC數目是50,目的地址是A,表示這50個BTC給A了。

    接下來,A想發25個BTC給B,怎麼構造這個交易呢?

    同樣的,我們需要一個輸入,這個輸入就是001交易的1號輸出,我們用001.1來表示。輸出分為兩個,第一個輸出編號1,表示要付25個BTC給B。第二個輸出編號2,表示剩下的BTC要還給A。

    大家可能會問了,輸入是50BTC,兩個輸出加起來才45個BTC,好像還少了5個BTC?沒錯,這個5個BTC就是給礦工的挖礦所得。

    接下來,A又繼續轉賬給C,同樣的道理,把一個一個的交易連接起來。

    從上面的例子我們可以看到,實際上錢是存在一個一個的交易記錄裏面的,那些未被花費的輸出,就叫做UTXO(Unspent Transaction Outputs)。

    那麼怎麼保證轉賬給B的錢,不會被其他的人消費呢?這就涉及到交易的加密過程了。

    我們以單個輸入和輸出為例來詳細了解一下交易的構成:

    上圖中,交易的輸入就是txid,也就是之前生成的還有未花費暑輸出的交易ID。output index就是交易的輸出id。

    一個非常重要的ScriptSig是輸入交易的驗證,表明這個用戶擁有這個賬戶的轉賬權限。

    輸出是一個腳本,只有滿足腳本運行條件的人才能花費這個output。這也就是ScriptSig需要驗證的腳本。

    我們看下腳本是怎麼做認證的吧。

    比特幣的標準輸出形式有兩種。Pay To Public Key Hash (P2PKH) 和 Pay To Script Hash (P2SH)。兩者的區別在於,一個是輸出到public key的hash,一個是輸出到任意的一個腳本輸出hash。

    為了保證輸出只能由特定的人來花費,一般的情況下是直接輸出到對方的public key hash。由於只有對方擁有的私鑰能夠生成這個public key hash,也就是說只有對方才能夠對這個輸出進行驗證。

    但每次都需要知道對方的public key hash還是比較麻煩的,更簡單的做法就是,發送者直接輸出到一個特定的hash值就行了,只要對方能夠生成這個hash就可以。

    下面的例子是一個P2PKH的腳本形式。

    P2PKH的輸出是一個腳本,裏面一個重要的值就是PK hash。

    怎麼驗證呢?

    驗證方提供兩個值,一個是sig,一個是PubKey。因為比特幣的虛擬機是棧結構的,我們先把這兩個值入棧。

    然後調用OP_DUP對最上層的PubKey進行拷貝,然後調用OP_HASH160算法來計算Pk Hash,然後將發送方保存的Pk Hash入棧。接下來調用OP_EQUALVERIFY對兩個PK Hash進行對比。

    如果對比成功,最後一步就是驗證Sig和PubKey是否匹配。

    如果都成功,說明接收方的確是這個PK Hash的擁有者。那麼對方就可以盡情使用了。

    擴展閱讀:圖靈非完備性

    和馮·諾伊曼同為現代計算機奠基人的阿蘭·圖靈(AlanTurin)在1950年提出了判定計算機能否像人那般實際“思考”的標準,也就是著名的“圖靈檢驗”。

    他設想一台超級計算機和一個人躲藏在幕後回答提問者的問題,而提問者則試圖分辨哪個是人哪個是計算機。

    圖靈爭辯說,假如計算機偽裝得如此巧妙,以致沒有人可以在實際上把它和一個真人分辨開來的話,那麼我們就可以聲稱,這台計算機和人一樣具備了思考能力,或者說,意識(他的原詞是“智慧”)。

    在可計算性理論里,如果一系列操作數據的規則(如指令集、編程語言、細胞自動機)按照一定的順序可以計算出結果,被稱為圖靈完備(turing complete)。

    比特幣腳本語言不是圖靈完備的,具有一定的局限性,它沒有循環語句和複雜的條件控制語句。

    總結

    本文介紹了比特幣的錢包和交易的概念,希望大家能夠喜歡。

    本文作者:flydean程序那些事

    本文鏈接:http://www.flydean.com/bitcoin-transactions/

    本文來源:flydean的博客

    歡迎關注我的公眾號:程序那些事,更多精彩等着您!

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

    ※想知道購買電動車哪裡補助最多?台中電動車補助資訊懶人包彙整

    南投搬家公司費用,距離,噸數怎麼算?達人教你簡易估價知識!

    ※教你寫出一流的銷售文案?

    ※超省錢租車方案

  • Spring中基於xml的AOP

    Spring中基於xml的AOP

    1、Aop 全程是Aspect Oriented Programming 即面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的同一維護的一種技術。Aop是oop的延續,是軟件開發中的 一個熱點,也是Spring框架中一個重要的內容。是函數式編程的一個衍生範例,利用Aop可以對業務邏輯各個部分進行分割,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用行,提高了開發效率。簡單的說就是把我們程序中的重複代碼抽取出來,在需要執行的時候,使用動態代理的技術,在不修改源碼的基礎上已有的方法進行增強,(使用動態代理的方式實現)

    相關術語

    JoinPoint:鏈接點 那些被攔截到的點,在spring中,這些點指的是方法,因為spring只支持方法類型的連接點

    Pointcut:切入點   是指我們要對哪些JoinPont進行攔截的定義

    Advice:通知/增強  攔截到Joinpoint之後所要做的事情就是通知

    通知類型:前置通知、後置通知、異常通知、最終通知、環繞通知

    Introduction:引介   是一種特殊的通知,在不修改類代碼的前提下,Introduction可以在運行期為類動態的添加一些方法或field

    Target:目標對象,代理的目標對象

    Weaving 織入   是指把增強應用到目標對象來創建新的代理對象的過程,spring採用動態代理織入,而AspectJ採用編譯期織入和類裝載期織入

    Proxy:代理,一類類被Aop織入增強后,就產生一個結果代理類

    Aspect:切面   是切入點和通知(引介)的結合

    在 spring 中,框架會根據目標類是否實現了接口來決定採用哪種動態代理的方式。

    基於XMl的AOP步驟

    1、創建Maven項目引入spring坐標

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.mingqi</groupId>
        <artifactId>SpringIOC</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.8.7</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.12</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    </project>

    2、創建業務層接口:

    package com.mingqi.services;
    public interface IAccountService {
        /**
         * 模擬登陸賬戶
         */
        void saveAccount();
    
        /**
         * 模擬更新賬戶
         * @param id
         */
        void updateAccount(int id);
    
        /**
         * 模擬刪除賬戶
         * @return
         */
        int deleteAccount();
    
    }

    3.創建業務層實現類

    package com.mingqi.services.impl;
    import com.mingqi.services.IAccountService;
    public class AccountServicesImpl implements IAccountService {
        public void saveAccount() {
            System.out.println("執行了保存");
        }
    
        public void updateAccount(int id) {
            System.out.println("執行了更新"+id);
        }
    
        public int deleteAccount() {
            System.out.println("執行了刪除");
            return 0;
        }
    }

    4、創建工具類

    package com.mingqi.utils;
    import org.aspectj.lang.ProceedingJoinPoint;
    /**
     * 用戶記錄日誌的工具類,裏面提供公共的代碼
     */
    public class Logger {
        /**
         * 用於打印日誌:計劃讓其在切入點方法執行前執行(切入點方法就是業務層方法)
         */
        public  void beforePrintLog(){
            System.out.println("Logger類中的pringLog方法開始記錄日誌了。。。");
        }
        public  void afterReturningPrintLog()
        {
            System.out.println("後置通知Logger類中的beforePrintLog方法開始記錄日誌了。。。");
        }
        /**
         * 異常通知
         */
        public void afterThrowingPrintLog()
        {
            System.out.println("異常通知Logger類中的afterThrowingPrintLog方法開始記錄日誌了。。。");
    
        }
        /**
         * 最終通知
         */
        public void afterPrintLog()
        {
            System.out.println("最終通知Logger類中的afterPrintLog方法開始記錄日誌了。。。");
        }
    
        /**
         * 環繞通知
         * 問題  當我們配置了環繞通知以後,切入點方法沒有執行,而通知方法執行了
         * 分析: 通過對比動態代理中的環繞通知代碼,發現動態代理中的環繞通知有明確的切入點方法調用,而我們的代碼中沒有
         * 解決: Spring 框架為我們提供了一個接口:ProceedingJoinPoint。該接口有一個方法proceed(),此方法就相當於明確調用切入點的方法
         *        該接口可以作為環繞通知的參數方法,在程序執行時,spring框架會為我們提供該接口的實現類供我們使用
         * spring中的環繞通知
         *      他是spring框架為我們提供的一種可以在代碼中手動控制增強方法何時會執行的方式
         * @param pjp
         * @return
         */
        public Object aroundPringLog(ProceedingJoinPoint pjp){
            Object rtValue = null;
            try{
                Object[] args = pjp.getArgs();//得到方法執行所需的參數
    
                System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。前置");
    
                rtValue = pjp.proceed(args);//明確調用業務層方法(切入點方法)
    
                System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。後置");
    
                return rtValue;
            }catch (Throwable t){
                System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。異常");
                throw new RuntimeException(t);
            }finally {
                System.out.println("Logger類中的aroundPringLog方法開始記錄日誌了。。。最終");
            }
        }
    }

    4、創建bean配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
           <!-- 配置spring的IOC,把service對象配置進來-->
           <bean id="accountSevice" class="com.mingqi.services.impl.AccountServicesImpl"></bean>
           <!-- spring 中基於xml的Aop配置步驟
             1、把通知Bean也交給spring來管理
             2、使用aop:config標籤表名開始aop的配置
             3、使用aop:aspect標籤表明配置切面
                 id屬性:是給切面提供一個唯一標識
                 ref屬性:是指定通知類的id
             4、在aop:aspect標籤的內部使用對應的標籤來配置通知的類型
                 我們現在的示例是讓printlog方法在切入點方法執行之前執行,所以是前置通知
                 aop:before:標識前置通知
                    method屬性: 用於指定Logger類中的方法哪個是前置通知
                    pointcut屬性: 用於指定切入點表達式,該表達式的含義指的是對業務層中的哪些方法增強
                    切入點表達式的寫法:
                       關鍵字:execution(表達式)
                       表達式:  訪問修飾符 返回值 包名.包名.包名....類名.方法名(參數列表)
                       標準的寫法: public void com.mingqi.service.impl.AccountServiceImpl.saveAccount()
                       訪問修飾符可以省略:void com.mingqi.service.impl.AccountServiceImpl.saveAccount()
                       返回值可以使用通配符,標識任意返回值:* com.mingqi.service.impl.AccountServiceImpl.saveAccount()
                       包名可以使用通配符,表示任意包,但是有幾級包就需要寫幾個*  *.*.*.*.*.AccountServiceImpl.saveAccount()
                       包名可以使用..代表當前包及其子包:* *.AccountServiceImpl.saveAccount()
                       類名和方法名都可以使用*來實現統配 * *..*.*();
                       參數列表: 可以直接寫數據類型:
                                     基本類型直接寫名稱:int
                                     引用類型寫包名.類名的方式: java.lang.String
                                    可以使用通配符來標識任意類型,單必須有參數
                                    可以使用..標識有無參數均可,有參數可以是任意類型
    
                          全通配寫法:
                        * *..*.*(..)
                       實際開發中 切入點表達式的通常寫法:
                              切到業務層實現類的所有方法,* com.mingqi.service.impl.*.*(..);
             -->
           <!-- 配置Logger類-->
           <bean id="logger" class="com.mingqi.utils.Logger"></bean>
           <!--使用aop:config標籤表名開始aop的配置-->
           <aop:config>
                  <aop:pointcut id="pt1" expression="execution(* com.mingqi.services.impl.*.*(..))"></aop:pointcut>
                  <!--使用aop:aspect標籤表明配置切面-->
                  <aop:aspect id="LogAdvice" ref="logger">
                         <!-- 配置前置通知:在切入點方法執行之前執行
                         <aop:before method="beforePrintLog" pointcut-ref="pt1"></aop:before>-->
    
                         <!-- 配置後置通知:在切入點方法正常執行之後值。它和異常通知永遠只能執行一個
                              <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>-->
                         <!-- 配置異常通知:在切入點方法執行產生異常之後執行。它和後置通知永遠只能執行一個
                             <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>-->
                         <!-- 配置最終通知:無論切入點方法是否正常執行它都會在其後面執行
                            <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>-->
                         <!-- 配置環繞通知 詳細的註釋請看Logger類中-->
                            <aop:around method="aroundPringLog" pointcut-ref="pt1"></aop:around>
                        </aop:aspect>
                 </aop:config>
           </beans>

    6、創建測試類

    package com.mingqi.test;
    import com.mingqi.services.IAccountService;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    public class SpringIoc {
        @Test
        public void TestAccount()
        {
            ApplicationContext ac= new ClassPathXmlApplicationContext("beam.xml");
            IAccountService accountService=(IAccountService) ac.getBean("accountSevice");
            accountService.saveAccount();
            accountService.updateAccount(22);
            accountService.deleteAccount();
        }
    }

     

     

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

    網頁設計一頭霧水該從何著手呢? 台北網頁設計公司幫您輕鬆架站!

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

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

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

    ※別再煩惱如何寫文案,掌握八大原則!

    網頁設計最專業,超強功能平台可客製化

  • 深入了解C#(TPL)之Parallel.ForEach異步

    深入了解C#(TPL)之Parallel.ForEach異步

    前言

    最近在做項目過程中使用到了如題并行方法,當時還是有點猶豫不決,因為平常使用不多, 於是藉助周末時間稍微深入了下,發現我用錯了,故此做一詳細記錄,希望對也不是很了解的童鞋在看到此文後不要再犯和我同樣的錯誤。

    并行遍歷異步表象

    這裏我們就不再講解該語法的作用以及和正常遍歷處理的區別,網上文章比比皆是,我們直接進入主題,本文所演示程序在控制台中進行。可能大部分童鞋都是如下大概這樣用的

    Parallel.ForEach(Enumerable.Range(0, 10), index =>
    {
        Console.WriteLine(index);
    });

     

    我們採取并行方式遍歷10個元素,然後結果也隨機打印出10個元素,一點毛病也沒有。然而我是用的異步方式,如下:

    Parallel.ForEach(Enumerable.Range(0, 10), async index =>
    {
        await AsyncTask(index);
    });
    static async Task<int> AsyncTask(int i)
    {
        await Task.Delay(100);
        
        var calculate = i * 2;
        
        Console.WriteLine(calculate);
    
        return calculate;
    }

    我們只是將并行操作更改為了異步形式,然後對每個元素進行對應處理,打印無序結果,一切也是如我們所期望,接下來我再來看一個例子,經過并行異步處理后猜猜最終字典中元素個數可能或一定為多少呢?

    var dicts = new ConcurrentDictionary<string, int>();
    
    Parallel.ForEach(Enumerable.Range(0, 10), async index =>
    {
        var result = await AsyncTask(index);
    
        dicts.TryAdd(index.ToString(), result);
    });
    
    Console.WriteLine($"element count in dictionary {dicts.Count}");

     

    如果對該并行方法沒有深入了解的話,大概率都會猜錯,我們看到字典中元素為0,主要原因是用了異步后引起的,為何會這樣呢?我們首先從表象上來分析,當我們在控制台上對并行方法用了異步后,你會發現編譯器會告警(主函數入口已用異步標識),如下:

    接下來我們再來看看調用該并行異步方法的最終調用構造,如下:

    public static ParallelLoopResult ForEach<TSource>(IEnumerable<TSource> source, Action<TSource> body);

    第二個參數為內置委託Action,所以我們也可以看出並不能用於異步,因為要是異步至少也是Func<Task>,比如如下方法參數形式

    static async Task AsyncDemo(Func<int,Task> func)
    {
        await func(1);
    }

    并行遍歷異步本質

    通過如上表象的分析我們得出并行遍歷方法應該是並不支持異步(通過最終結果分析得知,表述為不能用於異步更恰當),但是在實際項目開發中我們若沒有注意到該方法的構造很容易就會誤以為支持異步,如我一樣寫完也沒報錯,也就草草了事。那麼接下來我們反編譯看下最終實際情況會是怎樣的呢。

    進入主函數,我們已將主函數進行異步標識,所以將主函數放在狀態機中執行(狀態機類,<Main>d_0),這點我們毫無保留的贊同,接下來實例化字典,並通過并行遍歷異步處理元素集合併將其結果嘗試放入到字典中

    由上我們可以看到主函數是在狀態機中運行且構造為AsyncTaskMethodBuilder,當我們通過并行遍歷異步處理時每次都會實例化一個狀態機類即如上<<Main>b__0>d,但我們發現此狀態機的構造是AsyncVoidMethodBuilder,利用此狀態機類來異步處理每一個元素,如下

    最終調用AsyncTask異步方法,這裏我就不再截圖,同樣也是生成一個此異步方法的狀態機類。稍加分析想必我們已經知曉結果,AsyncTaskMethodBuilder指的就是(async task),而AsyncVoidMethodBuilder指的是(async void),所以對并行遍歷異步操作是將其隱式轉換為async void,而不是async task,這也和我們從其構造為Action得出的結論一致,我們知道(async void)僅限於基於事件的處理程序(常見於客戶端應用程序),其他情況避免用async void,也就是說將返回值放在Task或Task<T>中。當并行執行任務時,由於返回值為void,不會等待操作完成,這也就不難解釋為何字典中元素個數為0。

    總結

    當時並沒有過多的去了解,只是想當然的認為用了異步也沒出現編譯報錯,但是又由於沒怎麼用過,我還是抱着懷疑的態度,於是再深究了下,發現用法是大錯特錯。通過構造僅接受為Action委託,這也就意味着根本無法等待異步操作完成,之所以能接受異步索引其本質是隱式轉換為(async void),從另外一個角度看,異步主要用於IO密集型,而并行處理用於CPU密集型計算,基於此上種種一定不能用於異步,否則結果你懂的。

    本站聲明:網站內容來源於博客園,如有侵權,請聯繫我們,我們將及時處理

    【其他文章推薦】

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

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

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

    南投搬家公司費用需注意的眉眉角角,別等搬了再說!

    ※教你寫出一流的銷售文案?