大力發展再生能源,希臘渡假勝地蒂洛斯將成地中海首座綠能島

摘錄自2018年8月21日科技新報報導

希臘小島蒂洛斯(Tilos)可說是愛琴海著名渡假勝地,擁有豐富環境生態與優美的海洋景致,而近期該島將透過裝置量達 200KW 太陽光電與 800KW 風能,進一步成為地中海首座 100% 再生能源供電的綠能島嶼。

蒂洛斯島希望能透過綠能達成電力自給自足。目前技術人員則正最終測試再生能源系統,預計將在 2018 年下旬全面上路,屆時該渡假小島將可透過裝置量達 200KW 太陽能、800KW 風力發電與儲存容量(storage capacity)為 2.4MWh 的儲能系統達到 100% 綠能供電。

該島將採用 NaNiCl2 熔鹽儲能系統,可在日照強和風大情況下儲存綠能多餘電力,並於夜間與旅遊旺季等需求量大、發電量較低時釋出電力並維持電網供電,而為提升微電網管理系統,住家與企業也會安裝智慧電錶來計算用電尖峰的時間。

歐盟執行執委會表示,蒂洛斯將成為地中海第一個電力自主的再生能源島嶼,且由於大多海島都難以跟歐洲大陸電網相連,歐盟也打算將蒂洛斯再生能源計畫應用於其他島嶼。

本站聲明:網站內容來源環境資訊中心https://e-info.org.tw/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

ZooKeeper使用入門

ZooKeeper簡介

ZooKeeper是一個分佈式的,開源的分佈式應用程序協調服務,是Hadoop的子項目之一。它是一個為分佈式應用提供一致性服務的軟件,提供的功能包括:配置維護、域名服務、分佈式同步、組服務等。

安裝ZooKeeper

操作系統要求

操作系統 客戶端 服務端 原生客戶端 附加組件
GNU/Linux 開發/生產 開發/生產 開發/生產 開發/生產
Solaris 開發/生產 開發/生產 不支持 不支持
FreeBSD 開發/生產 開發/生產 不支持 不支持
Windows 開發/生產 開發/生產 不支持 不支持
Mac OS X 開發 開發 不支持 不支持

軟件要求

Java 8及Java 11以上版本(Java 9和10不支持)

硬件要求

此硬件資源為官網推薦的配置,實際開發過程中不需要這麼大,筆者測試1核1G內存20G硬盤的虛擬機即可運行。

  • 2核
  • 2G內存
  • 80G硬盤

下載安裝並進行單點配置

  1. 下載頁面地址:https://zookeeper.apache.org/releases.html
  2. 官網只提供tar.gz格式的壓縮包,windows下載后按照zip之類的解壓方式可能會導致解壓后的包無法使用,筆者使用Git帶的命令行執行linux的解壓命令解壓后使用,如果沒有安裝Git則建議使用虛擬機安裝Linux使用。以下是正確解壓和錯誤解壓后的對比。

  1. 解壓后的ZooKeeper默認是無法執行的,需要進行配置,將 apache-zookeeper-3.6.1/conf/zoo_sample.cfg複製一份並重命名為zoo.cfg,沒什麼特殊需要裡邊的配置項默認即可,筆者因為是在windows下使用,所以將datadir修改了。配置文件項說明如下:
配置項 說明
tickTime ZooKeeper使用的時間,單位毫秒,一般用於心跳檢測,而ZooKeeper中的最小session超時時間是此項的兩倍
dataDir 保留內存數據庫快照的地址,如果不單獨指定,事務日誌也會記錄在此
clientPort 服務端監聽的端口號
initLimit 集群中的follower服務器與leader服務器之間初始連接時的最大心跳數
syncLimit 集群中follower服務器與leader服務器之間通訊時的最大心跳數
  1. 配置完成后即可在bin目錄下執行對應的文件啟動了,Windows下為zkServer.bat,Linux下為zkServer.sh

ZooKeeper應用

通過zkCli進行使用

  1. ZooKeeper啟動后,可以通過bin目錄下自帶的客戶端進行訪問,Windows下為zkCli.bat,Linux下為zkCli.sh
  2. 啟動時默認連接localhost:2181,如果有需要連接遠程或其他端口的情況,可以如下添加參數:
zkCli.sh -server IP:Port
  1. 進入客戶端后執行help(此處是一個隨意的指令,只要不是zkCli支持的操作都可以)可查看其支持的操作,關於所有操作的介紹請參考官方頁面:https://zookeeper.apache.org/doc/current/zookeeperCLI.html

  2. 常用操作介紹:

  • 查看節點信息,節點路徑不能以“/”結尾
ls /
ls /zookeeper
  • 創建一個節點
create /test
create /test/testa
  • 查看節點狀態
stat /test
stat /test/testa
  • 刪除節點
# 刪除單個空節點
delete /test/testa
delete /test

# 級聯刪除
deleteall /test

*退出客戶端

quit

通過ZooKeeper客戶端使用

因為筆者的第一開發語言是Java,這裏以Java為例。常用的ZooKeeper Java客戶端用zkclient和Apache Curator兩種。zkclient是github上的一個開源項目,該項目在2018年10月2日後停止更新;Apache Curator是Apache基金會的開源項目,目前持續更新,推薦使用。常用的分佈式RPC框架DUBBO也在2019年1月份推出的2.7.0版本中將默認的ZooKeeper客戶端由zkclient切換為Apache Curator,此文中的示例也使用Apache Curator。

  1. 創建一個Maven項目,然後在pom.xml中引用Apache Curator,以下是筆者的文件內容,除了Apache Curator外添加了測試使用的junit並設置了編譯使用的java版本。
<?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>org.example</groupId>
    <artifactId>apache-curator</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.apache.curator/curator-recipes -->
        <dependency>
            <groupId>org.apache.curator</groupId>
            <artifactId>curator-recipes</artifactId>
            <version>4.3.0</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter -->
        <dependency>
            <groupId>org.junit.jupiter</groupId>
            <artifactId>junit-jupiter</artifactId>
            <version>5.6.2</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <encoding>UTF-8</encoding>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>
  1. 之後在src\test\java\目錄創建com\aotian\curator\test\Tester.java,文件基本框架如下,主要是創建一個空的測試類
public class Tester {

    @Test
    public void testCurator() {
      
    }

}
  1. 接下來就是使用Apache Curator提供的API對ZooKeeper進行訪問了。首先介紹下常用的API
  • 創建客戶端
RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy);
curatorFramework.start();
  • 檢查節點是否存在,存在的話返回Stat對象,不存在則返回null
curatorFramework.checkExists().forPath("/localhost/aotian");
  • 創建節點,forPath第二個參數可以指定節點內容,不設置時創建空節點
curatorFramework.create().creatingParentContainersIfNeeded().forPath("/localhost/aotian", message.getBytes());
  • 設置節點內容,僅適用於已存在的節點,否則會報錯
curatorFramework.setData().forPath("/localhost/aotian", message.getBytes());
  • 獲取節點信息,以下代碼錶示將獲取的節點信息保存到result對象。
Stat result = new Stat();
curatorFramework.getData().storingStatIn(result).forPath("/localhost/aotian");
  • 獲取節點內容
byte[] results = curatorFramework.getData().forPath("/localhost/aotian");
  1. 完整示例如下,結尾添加了線程睡眠的代碼,可以在睡眠時間內通過zkCli查看服務端中的內容。
    @Test
    public void testCurator() {
        // 創建客戶端
        RetryPolicy retryPolicy = new ExponentialBackoffRetry(1000, 3);
        CuratorFramework curatorFramework = CuratorFrameworkFactory.newClient("localhost:2181", retryPolicy);
        curatorFramework.start();
        // 定義節點內容
        String message = "testCurator";
        try {
            // 判斷節點是否存在不存在則創建,存在則設置指定值
            Stat a = curatorFramework.checkExists().forPath("/localhost/aotian");
            if (a == null){
                curatorFramework.create()
                        .creatingParentContainersIfNeeded()
                        .forPath("/localhost/aotian", message.getBytes());
            }else{
                curatorFramework.setData().forPath("/localhost/aotian", message.getBytes());
            }

            // 獲取節點信息
            Stat result = new Stat();
            curatorFramework.getData().storingStatIn(result).forPath("/localhost/aotian");
            System.out.println(result.getCtime());

            // 獲取節點內容
            byte[] results = curatorFramework.getData().forPath("/localhost/aoitan");
            System.out.println(new String(results));

            // 線程睡10S,這段時間內可以通過客戶端查看節點內的信息,結束后只能查看到空節點
            Thread.sleep(100000);
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            curatorFramework.close();
        }
    }

ZooKeeper集群搭建

ZooKeeper集群中包含兩種角色:Leader和Follower,因為ZooKeeper集群是半數節以上節點正常時才會正常提供服務,所以一般ZooKeeper集群中節點數量均為奇數。我們按照最小數量算,準備三台zookeeper服務器。

  1. 分別按照本文一開始的單機配置配置好三個ZooKeeper服務。個人聯繫或可以在同一台機器上部署三個ZooKeeper,只要解決端口衝突問題即可,實際生產過程中務必使用三台機器進行搭建,否則一旦機器出問題則整個集群癱瘓。
  2. 準備好三台ZooKeeper服務器之後我們準備開始集群的配置,首先我們需要規劃好ZooKeeper的ID,然後在datadir屬性對應的目錄下創建一個myid文件。然後在文件內寫上當前服務對應的ID,筆者規劃的是0、1、2,所以我需要添加的配置文件如下:
IP地址 文件路徑 文件內容
192.168.142.7 /tmp/zookeeper/myid 0
192.168.142.8 /tmp/zookeeper/myid 1
192.168.142.9 /tmp/zookeeper/myid 2

datadir屬性默認在/tmp目錄下,此目錄會被定期清理掉,生產環境不要使用。

3、配置完以上文件后,需要配置之前的zoo.cfg,在最後添加以下內容,其中server.*對應myid文件中的ID號,192.168.142.7是IP地址,2888是ZooKeeper集群的通訊端口,3888是集群選取Leader使用的端口。

server.0=192.168.142.7:2888:3888
server.1=192.168.142.8:2888:3888
server.2=192.168.142.9:2888:3888

4、最後檢查防火牆是否開放了2181、2888、3888端口,確認開放后啟動ZooKeeper即可。通過執行zkServer.sh status命令可以查看當前機器的狀態。

[root@centos-server-01 bin]# ./zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/apache-zookeeper-3.6.0/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: follower

[root@centos-server-02 bin]# ./zkServer.sh status
/usr/bin/java
ZooKeeper JMX enabled by default
Using config: /usr/apache-zookeeper-3.6.0/bin/../conf/zoo.cfg
Client port found: 2181. Client address: localhost.
Mode: leader

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

【其他文章推薦】

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

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

聚甘新

殼牌與IONITY聯手,歐洲據點大增、EV充電5分鐘搞定

  Thomson Reuters 27日報導,荷蘭皇家殼牌(Royal Dutch Shell plc)宣布與IONITY GmbH結盟、2019年將在歐洲(包括比利時、英國、法國、荷蘭、奧地利、捷克、匈牙利、波蘭、斯洛伐克和斯洛維尼亞)高速公路80個據點設置超高速電動車(EV)充電站。殼牌零售部門主管Istvan Kapitany表示,如果加上德國境內預計要增設的20座,兩年內殼牌在歐洲高速公路的加油站將有四分之一會提供高速充電服務。   殼牌指出,350 kW的充電功率搭配IONITY技術可讓EV充電時間縮短至5~8分鐘,遠比一般的數小時充電時間還要快。依據殼牌最樂觀的估算,全球EV車隊占整體車隊比重將從目前的1%成長至2025年底的10%,全球原油日需求量將因而縮減80萬桶。殼牌競爭對手BP 8月表示正與電動車製造商洽談合作案、預計在旗下加油站內將設置充電樁。根據歐洲替代燃料瞭望台的統計,2014~2017年期間歐洲EV充電樁成長將近三倍、逼近12萬大關。   英國金融時報報導,殼牌在全球各地擁有4萬座加油站、平均每天服務3千萬名客戶。殼牌將安裝的IONITY充電樁功率為350 kW、遠大於業界目前的標準規格(50kW)。IONITY成立於2016年,股東包括福特、BMW、戴姆勒、Volkswagen。   (本文內容由授權使用。首圖來源:public domain CC0)  

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

聚甘新

歐洲三航太巨擎 Airbus、Siemens、勞斯萊斯,聯手開發混動飛機

  在歐盟削減航空產業污染的壓力下,歐洲不少航太公司競相轉研製電動引擎,比如空中巴士公司(Airbus)和西門子公司(Siemens)已因此具有多年合作關係。最近,勞斯萊斯公司將加入成為第三方合作夥伴,並各自投資數千萬英鎊開發「E-Fan X」混合動力電動飛機,預計於 2025 年投入商用。   21 世紀從地面到天空都掀起了運輸業的電動革命,美國航太製造商巨擎波音(Boeing)前陣子剛收購了專門開發純電動飛機的 Aurora 公司,後者曾受邀協助 Uber 的飛行計程車計畫。   波音也投入資金支持一家新創公司 Zunum Aero,預計 5 年後推出一台小型油電混合動力飛機;歐洲易捷航空(EasyJet)則在今年 9 月宣布,將與美國創業公司 Wright Electric 合作開發一架全電動客機,但這兩個新創公司雖然雄心勃勃,卻絕對沒有空中巴士、勞斯萊斯和西門子這些老牌公司的經驗和技術。   身為歐洲最大軍火供應製造商空中巴士集團(Airbus Group)的旗下企業,歐洲客機製造商空中巴士(Airbus)自然不落人後,為了符合歐盟委員會(European Commission)訂下的 2050 年航空運輸業願景:飛機每公里二氧化碳排放量減少 60%、氮氧化物污染減少 90%、噪音減少 75%,它們曾玩過純電動飛機,近年則多與德國跨國工業集團西門子公司(Siemens)合作打造混動電動飛機。   最近,飛機發動機在全球市佔率僅次於通用電氣公司(GE)的勞斯萊斯(Rolls-Royce)將成為第三方合作夥伴,共同開發「E-Fan X」混合動力電動飛機,機體基於英國航太 146 型客機(BAe 146)。首要任務是將飛機的 4 台燃氣渦輪發動機改裝成 2 台電動機,他們將面臨熱效應、電控推力管理、電磁兼容性、海拔對電力系統的影響等各種挑戰。   3 家公司各自投資數千萬英鎊在不同領域,空中巴士負責整體整合,包括混合動力電動推進系統、電池的控制架構,以及飛行控制系統等;勞斯萊斯負責研製渦輪發動機、發電機和電力電子設備;西門子則負責提供逆變器、DC/DC 轉換器和配電系統,預計 2020 年進行全面試驗,2025 年投入商用。   電池與設備重量是飛機最需克服的障礙,如果發動機、發電機和電力電子系統可以成功整合減輕重量,未來大幅減少噪音的電動飛機將能安排更多夜間航班。   (合作媒體:。首圖來源:)    

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

聚甘新

電動車夯,立凱-KY 8月營收創歷史新高

電池正極材料廠立凱-KY公布8月合併營收為1.47億元,年成長62.6%,月增23.1%,創下單月歷史新高;累計其前8月營收8.77億元、年增24.03%;公司指出,8月營收攀高,主因是受惠產品價格持續走揚、以及車電事業部新增加技術服務收入。

立凱-KY並表示,公司在中國大陸與五龍電動車集團的合作亦有重要斬獲。9月4日在中國杭州開幕的二十國集團(G20)首腦峰會,中國作為接待地主國為體現國家綠色和可持續發展的理念,首次使用純電動汽車作為G20峰會首長貴賓接待用車,即採用210輛五龍旗下長江汽車自主研發的電動中巴車、電動商務車作為會議VIP接待用車和會議核心區域工作用車;而長江電動汽車不僅造型新穎,其科技配置、智慧駕馭模式更是為各國來賓所讚譽。

立凱-KY已於上月底前完成與五龍的資本合作案,雙方預計將在資金及技術方面合作,以加速拓展大陸電動車市場版圖。

(本文內容由授權提供)

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

聚甘新

推動電動車,中國要求加速住宅區充電設施建設

為確保住宅區居民的電動車充電需求,中國國家發改委、能源局、工信不予鑄件部聯合發出通知,要求各地區加速推廣住宅區的電動車充電基礎建設,並須推動既有之住宅區停車位的電氣化改造。

根據聯合通知,在既有停車位的改造方面,專用的固定車位需依照「一表一車位」之模式進行配套供電設施增加容量的改造,且每個停車位需配置適當的容量電能表。公用車位則需考量該區實際情形與電動車車主之充電需求,進行配套供電設施建設。

北京政府亦將針對住宅區停車位的電氣化建設提供專項資金等政策支持。

同時,聯合通知要求開始研究第三方充電服務企業、物業服務企業、車位產權方、業主委員會等多方共同參與住宅區充電基礎設施營運與市場化經營,並鼓勵導入集中改造、智慧充電管理、多用戶分時分享等營運模式,以提升營運水準。各地的主管部門亦可根據在地營運狀況推出合理的收費機制,以行程可永續經營的市場型態。

該通知同時要求推動住宅區充電設施的保險配套工作,保險涵蓋範圍包括:設施製造商、營運業務方、企業、用戶個人等。

中國將在京津冀魯、長江三角、珠江三角等三地的重點城市與各地能源發改委以及房地產主管部門合作推動示範性專案。

本站聲明:網站內容來源於EnergyTrend https://www.energytrend.com.tw/ev/,如有侵權,請聯繫我們,我們將及時處理

【其他文章推薦】

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

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

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

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

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

聚甘新

[經驗棧]C#監測IPv4v6網速及流量

1、前言

  最近做項目需要用到監測網速及流量,我經過百度和牆內谷歌都沒能快速發現監測IPV6流量和網速的用例;也經過自己的一番查詢和調試,浪費了不少時間,現在作為經驗分享出來希望大家指正。

2、C#代碼

using System.Net.NetworkInformation;
using System.Timers;

namespace Monitor
{
    public class MonitorNetwork
    {      
        public string UpSpeed { get; set; }   
        public string DownSpeed { get; set; }
        public string AllTraffic { get; set; }            
        private string NetCardDescription { get; set; }    
        //建立連接時上傳的數據量
        private long BaseTraffic { get; set; }    
        private long OldUp { get; set; }    
        private long OldDown { get; set; }
        private NetworkInterface networkInterface { get; set; }
        private Timer timer = new Timer() { Interval = 1000 };
    
        public void Close()
        {
            timer.Stop();   
        }
    
        public MonitorNetwork(string netCardDescription)
        {   
            timer.Elapsed += Timer_Elapsed;    
            NetCardDescription = netCardDescription;    
            timer.Interval = 1000;     
        }

        public bool Start()
        {
            networkInterface = null;    
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();    
            foreach (var var in nics)
            {
                if (var.Description.Contains(NetCardDescription))
                {
                    networkInterface = var;
                    break;
                }
            }    
            if (networkInterface == null)
            {
                return false;
            }
            else
            {    
                BaseTraffic = (networkInterface.GetIPStatistics().BytesSent +
                               networkInterface.GetIPStatistics().BytesReceived);    
                OldUp = networkInterface.GetIPStatistics().BytesSent;    
                OldDown = networkInterface.GetIPStatistics().BytesReceived;   
                timer.Start();    
                return true;
            }
    
        }

        private string[] units = new string[] {"KB/s","MB/s","GB/s" };

        private void CalcUpSpeed()
        {
            long nowValue = networkInterface.GetIPStatistics().BytesSent;    
            int num = 0;
            double value = (nowValue - OldUp) / 1024.0;
            while (value > 1023)
            {
                value = (value / 1024.0);
                num++;
            }   
            UpSpeed = value.ToString("0.0") + units[num];    
            OldUp = nowValue;    
        }
    
        private void CalcDownSpeed()
        {
            long nowValue = networkInterface.GetIPStatistics().BytesReceived;   
            int num = 0;
            double value = (nowValue - OldDown) / 1024.0;     
            while (value > 1023)
            {
                value = (value / 1024.0);
                num++;
            }    
            DownSpeed = value.ToString("0.0") + units[num];    
            OldDown = nowValue;    
        }
    
        private string[] unitAlls = new string[] { "KB", "MB", "GB" ,"TB"};
    
        private void CalcAllTraffic()
        {
            long nowValue = OldDown+OldUp;    
            int num = 0;
            double value = (nowValue- BaseTraffic) / 1024.0;
            while (value > 1023)
            {
                value = (value / 1024.0);
                num++;
            }   
            AllTraffic = value.ToString("0.0") + unitAlls[num];
        }

        private void Timer_Elapsed(object sender, ElapsedEventArgs e)
        {
            CalcUpSpeed();
            CalcDownSpeed();
            CalcAllTraffic();
        }
    }
}

3、胡說八道

  雖然沒能直接快速地百度到方法,但是實現這個需求的時候,心裏是有個譜,Windows系統能監測到這個網速和流量,沒理由實現不了,只需要一個方法將這個信息讀取出來就好。最後實現這個需求是利用了System.Net.NetworkInformation這個程序集,但是這個程序集沒有隻接提供網速監測的方法,而是提供了接收和發送數據量的屬性,需要自己計算出即使網速,所以這個網速不是特別的準確。

  這個程序集其實一開始就看到了,前輩方法中使用的是IPv4InterfaceStatistics類中的BytesReceived屬性和BytesSent屬性實現的,但是在這個程序集里沒有對應的IPv6類,恍恍惚惚。

  然後呢,我就下意識以為這個程序集比較老舊,不支持IPv6統計信息讀取,然後也是各種搜索無果,之後呢不死心想再來研究研究,東點點西瞅瞅,然後在NetworkInterface 類中發現了一個GetIPStatistics()方法,它的描述是“獲取此 NetworkInterface 實例的 IP 統計信息。”。

  然後就順理成章的事了,根據GetIPStatistics()返回的IPInterfaceStatistics實例中的BytesReceived屬性和BytesSent屬性就能獲取到收發的數據總量,然後根據這個信息就能計算出大約的網速。

  經測試,利用IPInterfaceStatistics實例是能讀取到IPv4和IPv6的總數據量的,因為這次的需求就是監測總量,如果需要單獨監測IPv6的可以用總量減去IPv4部分。

4、後記

​  老師以前喊我認真念書,我心想有百度還不夠嗎,再念能有百度聰明,有百度懂得多,後來漸漸明白,百度懂得多都是前輩的搬磚添瓦來的,共勉。

參考資料

  System.Net.NetworkInformation 命名空間

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

【其他文章推薦】

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

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

聚甘新

EOS基礎全家桶(十三)智能合約基礎

簡介

智能合約是現在區塊鏈的一大特色,而不同的鏈使用的智能合約的虛擬機各不相同,編碼語言也有很大差異。而今天我們開始學習EOS的智能合約,我也是從EOS初期一直開發合約至今,期間踩過無數坑,也在Stack Overflow上提過問(最後自己解決了),在實際生產中也積累了很多經驗,所以我會連續幾周分多次分享合約開發的經驗,今天先來點基礎的。

一些C++的編程基礎

EOS就是使用C++開發的,這也為它帶來了諸多好處,而合約也沿用C++作為開發語言,雖然合約中無法直接使用Boost等框架(你可以自己引入,但這也意味着合約會很大,會佔用大量賬號的內存),但是我們還是可以使用很多C++的小型庫,並伴隨着eosio.cdt的發展,融入了更多實用的合約功能。

如果你之前沒有使用C系列的開發語言做過開發,比如:C語言、C++或者是C#,那麼你需要先學習下C語言的基本語法和數據結構,這裏我不做展開,在我們的系列文章的開篇就介紹了我推薦的Learn EOS – c/c++ 教程英文版,有一定英語基礎的朋友可以直接看這個,其他朋友也可以在網上找一些C++的入門教程看下。

如果你已經有了一定的C語言基礎,那麼寫合約的話,你會發現需要的基礎也並不多,依葫蘆畫瓢就能寫出各種基礎功能了,所以,你並不需要擔心太多語言上的門檻,畢竟合約只是一個特定環境下運行的程序,你能用到的東西並不會很多。

CDT選擇

EOS的早期版本進行合約開發還沒有CDT工具,那時的合約藉助的是源碼中的工具eosiocpp,所以你看2018年的博客,進行合約編譯都是用它,但你現在是見不到了。隨着官方CDT的迭代,在CDT的1.4版本開始被官方推薦使用,CDT後面也經歷了幾個大的版本更新,逐步改善合約編寫方式,更加趨於簡潔、直觀。

但是不同的CDT版本,也意味着編譯器的不同,所以合約開發也會有所區別,比如一些語法變了,一些庫名稱變了,增加了一些新的標註……

我們的教程側重還是介紹最新的語法,所以推薦使用1.6以上的版本。我也會盡量在後面的介紹中補充說明老的CDT的寫法,方便大家對照網上其他老博客的合約。

來個HelloWorld

學習任何編程,我們都不能少了Mr.HelloWorld,先來給大家打個招呼吧。

#include <eosio/eosio.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract
{
public:
    using contract::contract;

    [[eosio::action]] void hi(name user)
    {
        print("Hello, ", user);
    }
};

  

基本合約結構及類型

hello合約就是一個最簡單的合約了,而且還有一個可調用的action為hi。我們首先還是來介紹下一個合約的程序結構吧。

  • 程序頭

包含了引入的頭文件、庫文件等,還有全局的命名空間的引入等。

#include <eosio/eosio.hpp>

using namespace eosio;

  

這裏eosio庫是我們的合約基礎庫,所有和eos相關的類型和方法,都在這個庫裏面,而這個庫裏面eosio.hpp是基礎,包含了contract等的定義,所以所有的合約都要引入。

【CDT老版本】早期cdt版本中庫名稱不是eosio,而是eosiolib

默認的,我們引入了eosio的命名空間,因為eosio的所有內容都是在這個命名空間下的,所以我們全局引入,會方便我們後續的代碼編寫。

  • 合約類定義

其實就是定義了一個class,繼承contract,並通過[[eosio::contract]]標註這個類是一個合約。使用using引入contract也是為了後續代碼可以更簡潔。

class [[eosio::contract]] hello : public contract{
public:
    using contract::contract;
}

  

【CDT老版本】早期cdt版本中直接使用了CONTRACT來定義合約類,比如:CONTRACT hello: public contract {}

  • action定義

寫一個public的方法,參數盡量用簡單或者是eosio內置的類型定義,無返回值(合約調用無法返回任何結果,除非報錯),然後在用[[eosio::action]]標註這個方法是一個合約action就行。

注意:action的名稱要求符合name類型的規則,name規則請看下面的常用類型中的說明。

[[eosio::action]]
void hi( name user ) {
    print( "Hello, ", user);
}

  

因為合約無法調試,所以只能通過print來打印信息,或者直接通過斷言拋出異常來進行調試。

【CDT老版本】早期cdt版本中直接使用ACTION來定義方法,比如:ACTION hi( name user ){}

  • 常用類型
類型 說明 示例
name 名稱類型,賬號名、表名、action名都是該類型,只能使用26個小寫字母和1到5的数字,特殊可以使用小數點,總長不超過13。 name("hi") 或者 "hi"_n
asset 資產類型,Token都是使用該類型,包含了Token符號和小數位,是一個複合類型,字符形式為1.0000 EOS asset(10000, symbol("TADO", 4)就是1.0000 TADO)
uint64_t 無符號64位整型,主要數據類型,表主鍵、name實質都是改類型 uint64_t amount = 10000000;
  • 內置常用對象或方法

在合約中,contract基類提供了一些方便的內置對象。

首先是get_self()或者是_self,這個方法可以獲取到當前合約所在的賬號,比如你把hello合約部署到了helloworld111這個賬號,那麼get_self()就可以獲取到helloworld111。

然後是get_code()或者是_code,這個方法可以獲取到當前交易請求的action方法名,這個在進行內聯action調用時可以用於判斷入口action。

最後是get_datastream()或者_ds,這個方法獲取的是數據流,如果你使用的是複雜類型,或者是自定義類型,那麼你無法在方法的參數上直接獲取到反序列化的變量值,你必須自己通過數據流來解析。

常用的還有獲取當前時間current_time_point(),這個需要引入#include <eosio/transaction.hpp>

數據持久化

當然,合約裏面,我們總會有些功能需要把數據存下來,在鏈上持久化存儲。所以我們就需要定義合約表了。

合約的表存在相應的合約賬號中,可以劃分表範圍(scope),每個表都有一個主鍵,uint64_t類型的,還可以有多個其他索引,表的查詢都是基於索引的。

這裏先提一句,表數據所佔用的內存,默認是合約賬號的內存,也可以使用其他賬號的,但需要權限,這個以後我們再介紹。

我們擴展一下hello合約。

#include <eosio/eosio.hpp>
#include <eosio/transaction.hpp>

using namespace eosio;

class [[eosio::contract]] hello : public contract
{
public:
    using contract::contract;

    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
    {
    }

    [[eosio::action]] void hi(name user)
    {
        print("Hello, ", user);

        uint32_t now = current_time_point().sec_since_epoch();

        auto friend_itr = friend_table.find(user.value);
        if (friend_itr == friend_table.end())
        {
            friend_table.emplace(get_self(), [&](auto &f) {
                f.friend_name = user;
                f.visit_time = now;
            });
        }
        else
        {
            friend_table.modify(friend_itr, get_self(), [&](auto &f) {
                f.visit_time = now;
            });
        }
    }

    [[eosio::action]] void nevermeet(name user)
    {
        print("Never see you again, ", user);

        auto friend_itr = friend_table.find(user.value);
        check(friend_itr != friend_table.end(), "I don't know who you are.");

        friend_table.erase(friend_itr);
    }

private:
    struct [[eosio::table]] my_friend
    {
        name friend_name;
        uint64_t visit_time;

        uint64_t primary_key() const { return friend_name.value; }
    };

    typedef eosio::multi_index<"friends"_n, my_friend> friends;

    friends friend_table;
};

  

可以看到,我們已經擴充了不少東西了,包括構造函數,表定義,多索引表配置,並完善了原先的hi方法,增加了nevermeet方法。

我們現在模擬的是這樣一個使用場景,我們遇到一個朋友的時候,就會和他打招呼(調用hi),如果這個朋友是一個新朋友,就會插入一條記錄到我們的朋友表中,如果是一個老朋友了,我們就會更新這個朋友的記錄中的訪問時間。當我們決定不再見這個朋友了,就是絕交了(調用nevermeet),我們就會把這個朋友的記錄刪除。

  • 表定義

首先我們需要聲明我們的朋友表。定義一個結構體,然後用[[eosio::table]]標註這個結構體是一個合約表。在結構體里定義一個函數名primary_key,返回uint64_t類型,作為主鍵的定義。

private:
    struct [[eosio::table]] my_friend
    {
        name friend_name;
        uint64_t visit_time;

        uint64_t primary_key() const { return friend_name.value; }
    };

  

我們這裏聲明了一個my_friend的表,合約的表名不在這裏定義,所以結構體的名稱不必滿足name的規則。我們定義了兩個字段,friend_name(朋友的名稱)和visit_time(拜訪時間),主鍵我們直接使用了friend_name,這個字段是name類型的,而name類型的實質就是一個uint64_t的類型(所以name的規則那麼苛刻)。

【CDT老版本】早期cdt版本中直接使用TABLE來定義合約表,比如:TABLE my_friend{}

  • 多索引表配置

合約里的表都是通過多索引來定義的,這是合約表的結構基礎。所以這裏才是定義表名和查詢索引的地方。

typedef eosio::multi_index<"friends"_n, my_friend> friends;

  

我們現在只介紹最簡單的單索引的定義,以後再介紹多索引的定義方式,這裏的"friends"_n就是定義表名,所以使用了name類型,之後my_friend是表的結構類型,typedef實質上就是聲明了一個類型別名,名字是friends的類型。

  • 構造函數

構造函數這裏並不是必須,但是為了我們能在全局直接使用合約表,所以我們要在構造函數進行表對象的實例化。

public:
    hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds), friend_table(get_self(), get_self().value)
    {
    }

private:
    friends friend_table;

  

這一段是標準合約構造函數,hello(name receiver, name code, datastream<const char *> ds) : contract(receiver, code, ds),合約類型實例化時會傳入receiver也就是我們的合約賬號(一般情況下),code就是我們的action名稱,ds就是數據流。

friend_table(get_self(), get_self().value)這一段就是對我們定義的friend_table變量的實例化,friend_table變量就是我們定義的多索引表的friends類型的實例。在合約里我們就可以直接使用friend_table變量來進行表操作了。實例化時傳遞的兩個參數正是表所在合約的名稱和表範圍(scope),這裏都使用的是當前合約的名稱。

  • 查詢記錄

查詢有多種方式,也就是多索引表提供了多種查詢的方式,默認的,使用findget方法是直接使用主鍵進行查詢,下次我們會介紹使用第二、第三等索引來進行查詢。find返回的是指針,數據是否存在,需要通過判斷指針是否是指到了表末尾,如果等於表末尾,就說明數據不存在,否則,指針的值就是數據對象。get直接返回的就是數據對象,所以在調用get時,就必須傳遞數據不存在時的錯誤信息。

auto friend_itr = friend_table.find(user.value);
if (friend_itr == friend_table.end())
{
    //數據不存在
}else
{
    //數據存在
}

  

我們在hi方法中先查詢了user是否存在。如果不存在,我們就添加數據,如果存在了,就修改數據中的visit_time字段的值為當前時間。

  • 添加記錄

多索引的表對象添加記錄使用emplace方法,第一個參數就是內存使用的對象,第二個參數就是添加表對象時的委託方法。

uint32_t now = current_time_point().sec_since_epoch();

auto friend_itr = friend_table.find(user.value);
if (friend_itr == friend_table.end())
{
    friend_table.emplace(get_self(), [&](auto &f) {
        f.friend_name = user;
        f.visit_time = now;
    });
}
else
{
    //數據存在
}

  

這裏先定義了一個變量now來表示當前時間,正是使用的內置方法current_time_point(),這個還是用了它的sec_since_epoch()方法,是為了直接獲取秒單位的值。

我們查詢后發現這個user的數據不存在,所以就進行插入操作,內存直接使用的合約賬號的,所以使用get_self(),然後對錶數據對象進行賦值。

  • 修改記錄

多索引的表對象修改記錄使用modify方法,第一個參數是傳遞需要修改的數據指針,第二個參數是內存使用的對象,第二個參數就是表對象修改時的委託方法。

friend_table.modify(friend_itr, get_self(), [&](auto &f) {
    f.visit_time = now;
});

  

我們將查詢到的用戶對象的指針friend_itr傳入,然後內存還是使用合約賬號的,委託中,我們只修改visit_time的值(主鍵是不能修改的)。

  • 刪除記錄
  • 多索引的表對象刪除記錄使用erase方法,只有一個參數,就是要刪除的對象指針,有返回值,是刪除數據后的指針偏移,也就是下一條數據的指針。
auto friend_itr = friend_table.find(user.value);
check(friend_itr != friend_table.end(), "I don't know who you are.");

friend_table.erase(friend_itr);

  

我們的示例中,將查詢到的這條數據直接刪除,併為使用變量來接收下一條數據的指針,在連續刪除數據時,你會需要獲取下一條數據的指針,因為已刪除的數據的指針已經失效了。

編譯

編譯我們再之前也有過介紹,安裝了eosio.cdt后,我們就有了eosio-cpp命令,進入到合約文件夾中,直接執行以下命令就會在當前目錄生成wasm和abi文件。

eosio-cpp -abigen hello.cpp -o hello.wasm

注意:替換命令中使用的hello.cpp為實際合約代碼文件名,而hello.wasm為實際合約的wasm文件名。

當然,編譯不通過的時候,你就要看看錯誤是什麼了,這可能會考驗一下你的C++功底。

發布

決定了要發布的賬號后,記得要購買足夠的內存和抵押足夠的資源。合約的內存消耗我們可以大致這樣估算,看下編譯好了的合約wasm文件有多大,然後乘以10,就是你發布到鏈上大概所需的內存大小了。

發布合約我們使用cleos set contract命令,其後跟合約賬號名和合約目錄,為了方便,我建議你把合約的目錄名保持和合約文件名一致。

cleos set contract helloworld111 ./hello -p helloworld111

這裏我們給出的代碼是將hello目錄下的hello合約發布到helloworld111。我這裏的文件夾是hello,裏面的abi和wasm也都是hello,這樣你不用手動指定合約文件了。

總結

至此,我想大家應該對合約的編寫有了一個大致的了解了,至少你可以參照着寫個簡單的合約出來了,這其中還有很多技巧和高級用法,我會在後續的文章中繼續和大家分享。

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

【其他文章推薦】

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

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

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

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

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

聚甘新

Java併發編程(05):悲觀鎖和樂觀鎖機制

本文源碼:GitHub·點這裏 || GitEE·點這裏

一、資源和加鎖

1、場景描述

多線程併發訪問同一個資源問題,假如線程A獲取變量之後修改變量值,線程C在此時也獲取變量值並且修改,兩個線程同時併發處理一個變量,就會導致併發問題。

這種并行處理數據庫的情況在實際的業務開發中很常見,兩個線程先後修改數據庫的值,導致數據有問題,該問題復現的概率不大,處理的時候需要對整個模塊體系有概念,才能容易定位問題。

2、演示案例

public class LockThread01 {
    public static void main(String[] args) {
        CountAdd countAdd = new CountAdd() ;
        AddThread01 addThread01 = new AddThread01(countAdd) ;
        addThread01.start();
        AddThread02 varThread02 = new AddThread02(countAdd) ;
        varThread02.start();
    }
}
class AddThread01 extends Thread {
    private CountAdd countAdd  ;
    public AddThread01 (CountAdd countAdd){
        this.countAdd = countAdd ;
    }
    @Override
    public void run() {
        countAdd.countAdd(30);
    }
}
class AddThread02 extends Thread {
    private CountAdd countAdd  ;
    public AddThread02 (CountAdd countAdd){
        this.countAdd = countAdd ;
    }
    @Override
    public void run() {
        countAdd.countAdd(10);
    }
}
class CountAdd {
    private Integer count = 0 ;
    public void countAdd (Integer num){
        try {
            if (num == 30){
                count = count + 50 ;
                Thread.sleep(3000);
            } else {
                count = count + num ;
            }
            System.out.println("num="+num+";count="+count);
        } catch (Exception e){
            e.printStackTrace();
        }
    }
}

這裏案例演示多線程併發修改count值,導致和預期不一致的結果,這是多線程併發下最常見的問題,尤其是在併發更新數據時。

出現併發的情況時,就需要通過一定的方式或策略來控制在併發情況下數據讀寫的準確性,這被稱為併發控制,實現併發控制手段也很多,最常見的方式是資源加鎖,還有一種簡單的實現策略:修改數據前讀取數據,修改的時候加入限制條件,保證修改的內容在此期間沒有被修改。

二、鎖的概念簡介

1、鎖機制簡介

併發編程中一個最關鍵的問題,多線程併發處理同一個資源,防止資源使用的衝突一個關鍵解決方法,就是在資源上加鎖:多線程序列化訪問。鎖是用來控制多個線程訪問共享資源的方式,鎖機制能夠讓共享資源在任意給定時刻只有一個線程任務訪問,實現線程任務的同步互斥,這是最理想但性能最差的方式,共享讀鎖的機制允許多任務併發訪問資源。

2、悲觀鎖

悲觀鎖,總是假設每次每次被讀取的數據會被修改,所以要給讀取的數據加鎖,具有強烈的資源獨佔和排他特性,在整個數據處理過程中,將數據處於鎖定狀態,例如synchronized關鍵字的實現就是悲觀機制。

悲觀鎖的實現,往往依靠數據庫提供的鎖機制,只有數據庫層提供的鎖機制才能真正保證數據訪問的排他性,否則,即使在本系統中實現了加鎖機制,也無法保證外部系統不會修改數據,悲觀鎖主要分為共享讀鎖和排他寫鎖。

排他鎖基本機制:又稱寫鎖,允許獲取排他鎖的事務更新數據,阻止其他事務取得相同的資源的共享讀鎖和排他鎖。若事務T對數據對象A加上寫鎖,事務T可以讀A也可以修改A,其他事務不能再對A加任何鎖,直到T釋放A上的寫鎖。

3、樂觀鎖

樂觀鎖相對悲觀鎖而言,採用更加寬鬆的加鎖機制。悲觀鎖大多數情況下依靠數據庫的鎖機制實現,以保證操作最大程度的獨佔性。但隨之而來的就是數據庫性能的大量開銷,特別是對長事務的開銷非常的占資源,樂觀鎖機制在一定程度上解決了這個問題。

樂觀鎖大多是基於數據版本記錄機制實現,為數據增加一個版本標識,在基於數據庫表的版本解決方案中,一般是通過為數據庫表增加一個version字段來實現。讀取出數據時,將此版本號一同讀出,之後更新時,對此版本號加一。此時,將提交數據的版本數據與數據庫表對應記錄的當前版本信息進行比對,如果提交的數據版本號等於數據庫表當前版本號,則予以更新,否則認為是過期數據。樂觀鎖機制在高併發場景下,可能會導致大量更新失敗的操作。

樂觀鎖的實現是策略層面的實現:CAS(Compare-And-Swap)。當多個線程嘗試使用CAS同時更新同一個變量時,只有其中一個線程能成功更新變量的值,而其它線程都失敗,失敗的線程並不會被掛起,而是被告知這次競爭中失敗,並可以再次嘗試。

4、機制對比

悲觀鎖本身的實現機制就以損失性能為代價,多線程爭搶,加鎖、釋放鎖會導致比較多的上下文切換和調度延時,加鎖的機制會產生額外的開銷,還有增加產生死鎖的概率,引發性能問題。

樂觀鎖雖然會基於對比檢測的手段判斷更新的數據是否有變化,但是不確定數據是否變化完成,例如線程1讀取的數據是A1,但是線程2操作A1的值變化為A2,然後再次變化為A1,這樣線程1的任務是沒有感知的。

悲觀鎖每一次數據修改都要上鎖,效率低,寫數據失敗的概率比較低,比較適合用在寫多讀少場景。

樂觀鎖並未真正加鎖,效率高,寫數據失敗的概率比較高,容易發生業務形異常,比較適合用在讀多寫少場景。

是選擇犧牲性能,還是追求效率,要根據業務場景判斷,這種選擇需要依賴經驗判斷,不過隨着技術迭代,數據庫的效率提升,集群模式的出現,性能和效率還是可以兩全的。

三、Lock基礎案例

1、Lock方法說明

lock:執行一次獲取鎖,獲取后立即返回;

lockInterruptibly:在獲取鎖的過程中可以中斷;

tryLock:嘗試非阻塞獲取鎖,可以設置超時時間,如果獲取成功返回true,有利於線程的狀態監控;

unlock:釋放鎖,清理線程狀態;

newCondition:獲取等待通知組件,和當前鎖綁定;

2、應用案例

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockThread02 {
    public static void main(String[] args) {
        LockNum lockNum = new LockNum() ;
        LockThread lockThread1 = new LockThread(lockNum,"TH1");
        LockThread lockThread2 = new LockThread(lockNum,"TH2");
        LockThread lockThread3 = new LockThread(lockNum,"TH3");
        lockThread1.start();
        lockThread2.start();
        lockThread3.start();
    }
}
class LockNum {
    private Lock lock = new ReentrantLock() ;
    public void getNum (){
        lock.lock();
        try {
            for (int i = 0 ; i < 3 ; i++){
                System.out.println("ThreadName:"+Thread.currentThread().getName()+";i="+i);
            }
        } finally {
            lock.unlock();
        }
    }
}
class LockThread extends Thread {
    private LockNum lockNum ;
    public LockThread (LockNum lockNum,String name){
        this.lockNum = lockNum ;
        super.setName(name);
    }
    @Override
    public void run() {
        lockNum.getNum();
    }
}

這裏多線程基於Lock鎖機制,分別依次執行任務,這是Lock的基礎用法,各種API的詳解,下次再說。

3、與synchronized對比

基於synchronized實現的鎖機制,安全性很高,但是一旦線程失敗,直接拋出異常,沒有清理線程狀態的機會。顯式的使用Lock語法,可以在finally語句中最終釋放鎖,維護相對正常的線程狀態,在獲取鎖的過程中,可以嘗試獲取,或者嘗試獲取鎖一段時間。

四、源代碼地址

GitHub·地址
https://github.com/cicadasmile/java-base-parent
GitEE·地址
https://gitee.com/cicadasmile/java-base-parent

推薦閱讀:Java基礎系列

序號 文章標題
A01 Java基礎:基本數據類型,核心點整理
A02 Java基礎:特殊的String類,和相關擴展API
B01 Java併發:線程的創建方式,狀態周期管理
B02 Java併發:線程核心機制,基礎概念擴展
B03 Java併發:多線程併發訪問,同步控制
B04 Java併發:線程間通信,等待/通知機制

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

【其他文章推薦】

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

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

聚甘新

JAVA設計模式 1【創建型】設計模式介紹、單例模式的理解與使用

數據結構我們已經學了一部分了。是該了解了解設計模式了。習慣了CRUD的你,也該了解了解這一門神器、我為啥要說是神器呢?

因為在大廠的面試環節、以及很多的比如

  • Springboot
  • Mybatis

等開源框架中、大量的使用到了設計模式。為了我們在之後學習源代碼的時候不再懵逼,為啥這代碼能這樣寫?為啥巴拉巴拉xxx

設計模式必須要肝完

簡介

設計模式,是一套被反覆使用、多數人知曉的、經過分類編目的、代碼設計經驗的總結
它是解決特定問題的一系列套路,是前輩們的代碼設計經驗的總結,具有一定的普遍性,可以反覆使用。其目的是為了提高代碼的可重用性、代碼的可讀性和代碼的可靠性。

總結下來說就是:一種設計經驗、一種設計套路

想一下,被前輩們總結下來的東西。使用這麼多年、凝結為精華的東西、該學

創建型

我們首先來了解一下什麼是創建型,創建型 作為設計模式的一種分類,是描述如何將一個對象創建出來的。

我們都知道,JAVA 作為一種面向對象編程,最關鍵的關鍵字new 用來實例化一個對象。創建型分類、則是描述:如何更好的創建出一個對象

單例模式理解

單例模式,從字面意思上了解就是:它只負責創建出一個對象。因此被稱為單例模式。理解一下:我們的計算機都會有一個任務管理器。而在一台windows 的電腦上。任何時候都只會實例化一個任務管理器對象。進而可以理解為單例模式

在一個任務管理器被調用的時候(對象已經被創建),再次使用ctrl+shift+esc 則任然返回這個已經被創建的任務管理器

UML 圖理解

可能我首次提到這個概念,所以簡介一下。

UML圖是用圖形化的方式表示軟件類與類之間的關係。用圖形化的方式,展示出眾多類之間的關聯關係。

類圖

如下圖,我用圖形的方式、描述了一個任務管理器類JobManagement.class
它有一個 私有化private的屬性management 類型為本身。
它有一個 公開的public 的方法getManagement() 返回類型為本身

  • 常用的類型修飾符就是 + 與 –

注意:“可見性”表示該屬性對類外的元素是否可見,包括公有(Public)、私有(Private)、受保護(Protected)和朋友(Friendly)4 種,在類圖中分別用符號+、-、#、~表示。
http://c.biancheng.net/view/1319.html

關聯關係

關聯關係就是用來表示:多個類之間存在怎麼樣的關係的表示方法。常用箭頭來表示。

虛線箭頭 依賴關係

虛線箭頭用來表示依賴關係 從使用類指向被依賴的類。這裏使用的類就是我們的main 方法。而被依賴類則是我們的任務管理器對象

菱形箭頭 聚合關係

聚合管理作為一種強關聯管理。一般用於成員變量的引用。

http://c.biancheng.net/view/1319.html

單例模式的特點

  • 對象只會被創建一次,並且重複使用
  • 全局提供一個訪問點。靜態訪問點
  • 構造方法私有

學以致用

public class JobManagement {

    private static volatile JobManagement management;

    private JobManagement() {

    }

    public static synchronized JobManagement getManagement() {

        if (null == management) {
            System.out.println("未創建任務管理器,正在創建。。");
            management = new JobManagement();
        } else {
            System.out.println("已經存在創建的任務管理器");
        }
        return management;
    }
}

任務管理器對象包含以及靜態類型的自身對象引用。以及將自身的構造方法進行私有化、使得外部無法直接創建對象。而需要這個對象,則需要調用get()方法進行獲取。

  • volatile 關鍵字將屬性在所有線程中同步

懶漢模式

	if (null == management) {
            System.out.println("未創建任務管理器,正在創建。。");
            management = new JobManagement();
        } 

懶漢模式則是在對象首次被訪問的時候才進行創建的。否則、若這個對象從未被引用、則對象是不會被創建的。而餓漢模式,剛剛相反。

餓漢模式

private static JobManagement management = new JobManagement();

餓漢模式則是則是在類被虛擬機加載的時候就創建一個示例出來。這樣在訪問之前就已經有對象被創建、線程也是安全的。

測試使用

    public static void main(String[] args) {

        JobManagement management1 = JobManagement.getManagement();
        System.out.println(management1);
        JobManagement management2 = JobManagement.getManagement();
        System.out.println(management2);

    }
----------------------------
未創建任務管理器,正在創建。。
JobManagement@1b6d3586
已經存在創建的任務管理器
JobManagement@1b6d3586

小結

單例模式在Java 的學習中還是有很多地方會使用到,對於我們學習的第一個,也是最簡單的模式,也是最常用的模式。記住它的特點:

  • 構造方法私有
  • 提供一個全局訪問點

參考

http://c.biancheng.net/view/1338.html

歡迎關注

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

【其他文章推薦】

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

台北網頁設計公司這麼多該如何選擇?

※智慧手機時代的來臨,RWD網頁設計為架站首選

※評比南投搬家公司費用收費行情懶人包大公開

※回頭車貨運收費標準

聚甘新