abp(net core)+easyui+efcore實現倉儲管理系統——ABP WebAPI與EasyUI結合增刪改查之一(二十七)

 



 

一.前言

       通過前面的文章的學習,我們已經有實現了傳統的ASP.NET Core MVC+EasyUI的增刪改查功能。本篇文章我們要實現了使用ABP提供的WebAPI方式+EasyUI來實現增刪改查的功能。本文中我們將不在使用DataGrid表格控件,而是使用樹形表格(TreeGrid)控件。

二、樹形表格(TreeGrid)介紹

       我先上圖,讓我們來看一下功能完成之後的組織管理信息列表頁面。如下圖。

       這個組織管理列表頁面使用TreeGrid來實現的。我們接下來介紹一下TreeGrid。

     首先、在定義TreeGrid時有兩個屬性必須要有一個是idField,這個要唯一;另一個是treeField的定義,這是樹節點的值,必須要有。 

     其次、easyui加載treegrid的json數據格式有三種,我就介紹我常用的這種。其他兩種方式,請查看easyui的相關文檔。

  {"total":7,"rows":[

           {"id":1,"name":"All Tasks","begin":"3/4/2010","end":"3/20/2010","progress":60,"iconCls":"icon-ok"},      
{"id":2,"name":"Designing","begin":"3/4/2010","end":"3/10/2010","progress":100,"_parentId":1,"state":"closed"},
{"id":21,"name":"Database","persons":2,"begin":"3/4/2010","end":"3/6/2010","progress":100,"_parentId":2},
{"id":22,"name":"UML","persons":1,"begin":"3/7/2010","end":"3/8/2010","progress":100,"_parentId":2}, {"id":23,"name":"Export Document","persons":1,"begin":"3/9/2010","end":"3/10/2010","progress":100,"_parentId":2},
{"id":3,"name":"Coding","persons":2,"begin":"3/11/2010","end":"3/18/2010","progress":80},
{"id":4,"name":"Testing","persons":1,"begin":"3/19/2010","end":"3/20/2010","progress":20} ],"footer":[ {"name":"Total Persons:","persons":7,"iconCls":"icon-sum"} ]}

     下面介紹一下上面數據中的幾個重要屬性:

     1)  _parentId :字段_parentId必不可少,且名稱唯一。記得前面有“_” ,他是用來記錄父級節點,沒有這個屬性,是沒法展示父級節點 其次就是這個父級節點必須存在,不然信息也是展示不出來,在後台遍歷組合的時候,如果父級節點不存在或為0時,此時 _parentId 應該不賦值,或設為“”。如果賦值 “0” 則在表格中不显示數據。

    2) state:是否展開

     3) checked:是否選中(用於複選框)

    4) iconCls:選項前面的圖標,如果自己不設定,父級節點默認為文件夾圖標,子級節點為文件圖標

 

    下面我們來開始實現組織管理頁面的相關功能。首先我們要創建一個組織信息實體。

三、創建Org實體

       1. 在Visual Studio 2017的“解決方案資源管理器”中,右鍵單擊“ABP.TPLMS.Core”項目的“Entitys”文件夾,在彈出菜單中選擇“添加” >

 > “類”。 將類命名為 Org,然後選擇“添加”。

      2.創建Org類繼承自Entity<int>,通過實現審計模塊中的IHasCreationTime來實現保存創建時間。根據TreeGrid所需要的數據格式的要求。代碼如下:

using Abp.Domain.Entities;
using Abp.Domain.Entities.Auditing;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Text;
 

namespace ABP.TPLMS.Entitys
{

   public partial class Org : Entity<int>, IHasCreationTime
{
      int m_parentId = 0;
        public Org()
        {

            this.Id = 0;
            this.Name = string.Empty;
            this.HotKey = string.Empty;
            this.ParentId = 0;
            this.ParentName = string.Empty;

            this.IconName = string.Empty;

            this.Status = 0;

            this.Type = 0;

            this.BizCode = string.Empty;
            this.CustomCode = string.Empty;
            this.CreationTime = DateTime.Now;
            this.UpdateTime = DateTime.Now;
            this.CreateId = 0;

            this.SortNo = 0;

        }

        [Required]
        [StringLength(255)]
        public string Name { get; set; }

        [StringLength(255)]
        public string HotKey { get; set; }

        public int ParentId { get { return m_parentId; } set { m_parentId = value; } }

        [Required]
        [StringLength(255)]
        public string ParentName { get; set; }

        public bool IsLeaf { get; set; }

        public bool IsAutoExpand { get; set; }

        [StringLength(255)]
        public string IconName { get; set; } 

        public int Status { get; set; }

        public int Type { get; set; }

        [StringLength(255)]
        public string BizCode { get; set; }

        [StringLength(100)]
        public string CustomCode { get; set; }

        public DateTime CreationTime { get; set; }
        public DateTime UpdateTime { get; set; }
        public int CreateId { get; set; }

        public int SortNo { get; set; }
public int? _parentId {
            get {
                if (m_parentId == 0)                

                {
                    return null;
                }

                return m_parentId;
            }           

        }
    }
}

      3.定義好實體之後,我們去“ABP.TPLMS.EntityFrameworkCore”項目中的“TPLMSDbContext”類中定義實體對應的DbSet,以應用Code First 數據遷移。添加以下代碼

 

using Microsoft.EntityFrameworkCore;
using Abp.Zero.EntityFrameworkCore;
using ABP.TPLMS.Authorization.Roles;
using ABP.TPLMS.Authorization.Users;
using ABP.TPLMS.MultiTenancy;
using ABP.TPLMS.Entitys;
 

namespace ABP.TPLMS.EntityFrameworkCore
{

    public class TPLMSDbContext : AbpZeroDbContext<Tenant, Role, User, TPLMSDbContext>
    {

        /* Define a DbSet for each entity of the application */
    
        public TPLMSDbContext(DbContextOptions<TPLMSDbContext> options)
            : base(options)

        {
        }

        public DbSet<Module> Modules { get; set; }
        public DbSet<Supplier> Suppliers { get; set; }
  public DbSet<Cargo> Cargos { get; set; }
          public DbSet<Org> Orgs { get; set; }

    }
}

 

 

 

      4.從菜單中選擇“工具->NuGet包管理器器—>程序包管理器控制台”菜單。

     5. 在PMC中,默認項目選擇EntityframeworkCore對應的項目后。輸入以下命令:Add-Migration AddEntityOrg,創建遷移。如下圖。

 

       6. 在上面的命令執行完畢之後,創建成功后,會在Migrations文件夾下創建時間_AddEntityOrg格式的類文件,這些代碼是基於DbContext指定的模型。如下圖。

 

     7.在程序包管理器控制台,輸入Update-Database,回車執行遷移。執行成功后,如下圖。

 

     8. 在SQL Server Management Studio中查看數據庫,Orgs表創建成功。

 

 

 

 

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

極*Java速成教程 – (1)

序言

眾所周知,程序員需要快速學習新知識,所以就有了《21天精通C++》和《MySQL-從刪庫到跑路》這樣的書籍,Java作為更“高級”的語言也不應該落後,所以我就寫一個《極·Java速成教程》,讓想學的人能夠快速學習Java(手斜)。
本文大概分三大部分:

  • 編程基礎知識
    • 面向對象思想
    • 數據類型
    • 運算符
    • 流程控制
  • Java語言基礎
  • Java高級特性

本文請逐字理解文本內容,本文僅供理解和學習Java編程,不可作為概念性參考

本文是閱讀《Java編程思想》后的讀後感
本文着重於理解某種思想,所以些許描述顯得晦澀,意會即可

面向對象思想

何為面向對象

Java是一個從設計之初就遵循面向對象思想的語言,因此而有着諸多面向對象的特性,那麼問題來了,面向對象指的是什麼?
面向對象,不是指的臉超女朋友,而是說編程的時候,你所操作的一切事物都是“對象”,假如說Java程序員是個搬磚工,那Java操作的對象就是搬磚工手裡的磚,一塊磚就是一個對象,一堆轉就是一堆對象,諸多同樣的對象可以用一個概念來概括,這個概念就是“”。
先在我們可以思考磚是個什麼東西,也就是磚的“概念(類)”:磚,由土燒制而成,可以用來砌牆、鋪地和墊桌角,有一定的重量和體積,能被搬被砌。仔細分析這句定義,可以發現能分為4小句:

  • 磚是由土燒制而成。也就是說,一個類都是從另一個或多個類繼承發展而來,也就是“繼承(extend)和組合自另一個或多個類”,在Java中,所有類都繼承和擴展自Object類,在自己寫的程序中,一個類可以繼承和擴展自官方的類,別人寫的類和自己寫的類。
  • 磚可以用來砌牆、鋪地和墊桌角。也就是同一個類可以根據不同的需求去加工處理,修改為不同的“形狀”,添加不同的屬性,在不同的地方起到不同的作用。
  • 磚由一定的重量和體積。也就是說每個類都有其屬性,但是對於同一種屬性,他們的值未必相同,正如每個磚的重量和體積都可以不同。
  • 磚能被搬被砌。也就是說每個類都有其功能,屬性是一個類靜態的描述,而功能(或者說方法)則是一個類動態的描述。

將許多個“磚”一樣的對象搭配組合,堆壘到一起,就可以構建起程序的這棟房屋,實現整體的功能。

Java中類的幾個特點

Java中的類有幾個特點

  1. 單根繼承,也就是說某一個類只能直接繼承自另一個類,就像磚只能由土燒制而成,即土→磚,然而除了土,磚的燒制過程中還需要加其他的配料比如外面要燒上去一層琉璃,那就得在磚本身的屬性以上添加一個琉璃的屬性,琉璃的屬性是額外的,獨立無關的,但是他們組合起來實現了更多的功能。也就是說除了直接繼承,還可以添加其他類到一個類中,這種方法叫通過接口(interface)進行繼承。
  2. 訪問控制。對於一個類來說,你只能看到一個類想讓你看到的部分。就像一個磚,你只能看到磚的表面,不能看到它的內里,除非敲開他,但是在Java中,這是不被允許的。一個類想讓你看到的部分和不想讓你看到的部分由不同的關鍵詞進行控制:
    • public 對於所有對象都公開的內容
    • private 只有自己可見的內容
    • protected 只有自己和自己的子孫可以看到的內容
    • default 只有自己的親戚(同一個包)可以看到的內容
      我們不用關心他們內部什麼樣子,我們只需要關心我們需要的,暴露在外面的東西,以及對象能正常工作就行。
  3. “像”就是“是”。對於一個類來說,如果長得像一個東西,那就可以看作是一個東西。就像兒子像父親,父親能幹的兒子也能幹一樣,一塊防火轉是一個磚,那他也有磚的所有屬性,也能被壘起來。而一個具有壘起來方法的對象看上去能壘起來,那他就能像磚一樣被壘起來。
  4. static屬性。對於一個類來說,有的屬性和方法可以是static也就是靜態的,這種屬性和方法不是描述一個類的對象的,而是描述一個類的。就比如可以對所有的磚執行統計數量操作,對於一個磚頭讓他統計同類的數量是不恰當的,但是對於一堆轉,讓這堆磚告訴你有多少塊是可行的。
  5. 對象生命周期管理。Java語言的使用者不用關心Java對象是怎麼誕生的,怎麼消亡的,只需要在需要時new一下,創建一個對象即可,使用完以後的垃圾丟到哪裡,那是Java高級特性需要研究的東西。
  6. Java中的類之上具有包(package)的結構,包是具有層次的,如java.lang.Math就表示java下的lang包下的Math類,這個層次結構可以有好多層,一層一層深入,所以可能這個名字會很長。在使用某一個包中的某一個類的某些內容時,需要使用import進行導入,如果需要導入某個包下的全部,可以使用*通配符。,如java.lang.*。

Java編程需要關心的點

只要想好這個類和它的對象應該怎麼設計,怎麼正常運行即可,其他語言的包袱,甩掉即可

數據類型

一切都是對象,而根基只需要幾個

當Java使用者需要一個對象時,這個對象在內存中的位置時我們不用管也沒法管的。但這個對象是怎麼來的,卻是可以思考的。就像房子是磚壘成的,磚是由土燒成的,那土是什麼構成的呢,這麼追究下去,追究到最後就成了重子和輕子了,但是我們不必追求這麼細,就像Java中我們知道一切對象都有Object這個“祖宗”,但是我們不需要凡事都請動這個祖宗,它有幾個子女是我們常用的,也就是“基本類型”:

類型 大小 包裝器 初始值
boolean true/false Boolean false
char 16 bit Character ‘\u0000’
byte 8 bit Byte (byte)0
short 16 bit Short (short)0
int 32 bit Integer 0
long 64 bit Long 0L
float 32 bit Float 0.0f
double 64 bit Double 0.0d
void Void void

這些基本類型的大小都與其位數n有關,最小值為-2n-1,最大值為2n-1-1,float和double遵循IEEE754標準。
除了基本類型,還有字符串String,高精度整數BigInteger和高精度浮點數BigDecimal等常見類。

還有一些方便的類型可以使用

String

字符串是一個常用的類型,可以用來儲存一個字符序列。字符串類型在內存中是一個不可變對象,也就是說當一個String s="ABC"字符串在內存中創建后,它就不可以被修改了,如果執行了s+="DEF"的操作,那這個s引用實際上是指向了一個新的”ABCDEF”對象,它與原來的”ABC”對象已經沒有關係了。
字符串變量的比較是不可以直接用==判斷的。==是用來進行引用對比的運算符,而兩個內容一樣的字符串引用未必在內存中指向同一個對象。因此,String對象的比較要使用equals()方法進行。

enum

enum是枚舉對象,是一個有限的可以n選1的對象的集合,可以在switch語句內使用。形如:

public enum Spiciness{
    NOT,MILD,MEDIUM,HOT,FLAMING
}

枚舉類型的實例都是常量,可以使用枚舉類型名.枚舉常量的方法選擇常量的值。它內部創建了toString()方法,所以可以很方便地打印枚舉常量的值,還提供了ordinal()方法,可以獲取某個枚舉常量在枚舉類型中的順序。以及靜態的 values()方法,可以按照枚舉常量的聲明順序獲取由這些常量值構成的數組。

對象應當被管理

一個對象,給他個稱號,就可以根據這個名字喊他做各種事,就像家裡人可以喊你小名讓你幹活一樣。但是當出了家裡,可能有好多人跟你重名,這會帶來困擾,因此是不被允許的,而在外面叫你的小名,這也會帶來尷尬,也就是說,這個名字超出了作用域,在Java中,一個稱號(或者說引用)的作用域是從這個命名的聲明到所在花括號的結尾。當然如果不關心這一點也沒問題,因為IDE會提醒你這裏出了問題。
當一個對象有了它的稱呼以後,就可以安排他做好多的事情了,無論誰喊,只要是喊到,就可以用。在作用域範圍內的所有調用都可以通過這個名字實行,而你也可以再給這個對象起另一個名字,這多個名字都是指的同一個對象,當處理不當時,這會帶來一些問題,比如一個喊你張三的人給你圖了個花臉,別人喊你李四把你喊過來以後也會看到一個花臉。
對於一堆相同類型的對象,可以用一個容器裝住他們,方便我們管理和使用,Java中提供了多種類型的容器,有最基本的數組,也就是一串連續的空間去裝一樣的對象,還有其他的諸如set和map之類的容器可以裝,這一點可以在後面進行詳細討論。

運算符

運算符按照字面語義理解就好

運算符有其優先級,也有短路的特徵。優先級就是說先乘除后加減,短路就是邏輯運算時當直接可以產生結果時就不再繼續運算。
運算符有以下幾個:
+。加號,数字的累加,字符串的連接,將較小類型的数字提升為int類型。
-。減號,数字相減。負號。
*。乘號,数字相乘。
/。除號,数字相除,給出結果的整數部分,直接截斷小數,不會四舍五入。
%。求余,数字相除求餘數。
++自增,–自減,當出現在對象前面時先運算再輸出值,當出現在對象後面時先輸出值再運算。
>,<,>=,<=。大於,小於,大於等於,小於等於。顧名思義。
==判斷相等,對於Java來說,這是容易使人困擾的一點,因為除了在判斷基本類型對象是否相等時以外,==僅僅為判斷別名所指向的對象是否為同一個,而不關心對象的屬性是否相等。如果要判斷String等官方封裝好的類型,就要用到equals()方法,但是對於自己定義的類來說,如果沒有重新定義equals()方法,就會和==一樣,對引用對象進行判斷,而不是對對象的屬性進行判斷。
&&,||,!。邏輯運算符與或非,對boolean值進行判斷,兩邊都true時&&結果為真,兩邊有一個為true時||結果為真,!是對boolean對象的邏輯值進行顛倒。
&,|,^,~。按位操作符,對数字類型進行按位的與,或,異或和非操作,與,或,異或是對兩個對象進行操作,非是對一個對象進行操作。
<<,>>,>>>。移位操作符,就是將操作數按位進行移位,A※※B就是將A往箭頭方向移B位。如果是負數,>>在左側補1,>>>在左側補0。
=。数字賦值,或者是給一個對象起別名,或者是將另一個別名添加到一個對象上。
+=,-=,*=,/=,&=,|=,^=,>>=,>>>=,<<=。比較類似,a※=b就是a=a※b。當>>>=byte和short時,轉化成int進行處理然後截斷,可能產生不正確的結果。
指數計數法:AeB代表了Ax10B,如1.39e-43f代表float的1.39×10-43
?:。這是個三元運算符,如A?B:C就表示如果A的邏輯結果為真,就執行B,否則就執行C。
直接常量,就是直接的數,如200D,0x200。其規則是如果後面跟D表示double,F表示float,L表示long,前面跟0表示8進制數,0x表示16進制數。

操作符使用時容易出現的問題

類型轉換

(A)B可以將B強制轉化為A類型,如果是從較小的類型轉化為較大的類型,將不會帶來問題,但如果是從較大的類型轉化為較小的類型,則會帶來信息丟失的問題。自定義類的轉換同理。

截尾和舍入

對於浮點數來說,強制轉化為整形時,會捨棄小數部分,如果要獲取捨入的結果,需要使用java.lang.Math中的round()方法。

流程控制

Java是一種面向對象的語言,但其執行過程依舊時順序的,在順序執行的程序中,會進行一些選擇分支和循環操作。

選擇

Java中可以使用if-else和switch進行分支選擇。

if-else

形如

if(A){
    B
}else{
    C
}

就是如果判斷條件A為真,則執行B,否則執行C。

switch

形如

switch(A){
    case 1: B;
    case 2: C;break;
    case 3: D;break;
    case 4: E;break;
···
}

即如果判斷條件A為1,則執行B然後執行C然後中斷該代碼塊,如果判斷條件為2則執行C然後中斷該代碼塊,如果判斷條件為3則執行D然後中斷該代碼塊,等等。

循環

循環有3種形式:while,do-while和for。

while

形如

while(A){
    B
}
對判斷條件A進行判斷,當判斷條件為A時,多次執行B代碼。

do-while

形如

do{
    B
}while(A)

先執行一次B代碼,然後對判斷條件A進行判斷,當判斷條件A為真時,循環至花括號頭部執行B代碼。

for

形如

for(int i=0,j=0;i<100;i++,j++){
    B
}

首先對第一個;前面的部分執行,常見於循環控制對象進行初始化,多個對象可以使用,進行區分,然後對;間的循環控制條件進行判斷,如果條件為真則執行下方代碼塊,最後對;后的第三部分執行,比如對循環對象進行自增操作等。實例中就是先對i和j進行初始化為0,然後判斷i是否小於100,如果為真則執行B代碼,最後執行i自增和j自增,然後判斷i是否小於100,如果為真繼續循環,該代碼最後將執行B代碼段100次。
在編程時,循環常在數組等可迭代對象上進行。可迭代就是可以從前到后順序走一遍。對此,Java提供了一些便利的語法方便開發。形如

容器/數組 A;
for(int i:A){
    B
}

就是說對於容器或者數組A,迭代遍歷一遍,i會在每次循環中引用到A中的每個對象,然後執行B代碼塊。假如容器/數組A中有100個對象,那B就會被執行100次。

返回

return

結束該語句所在函數,返回返回值。
返回值類型由函數定義決定,如果不匹配且無法兼容則會IDE報錯。

Java的與眾不同

Java的流程控制中沒有goto語句,但是通過break和continue語句,實現了從深層循環中跳出的功能。通過在循環前標記label:,然後使用break labelcontinue label,即可跳出循環到指定位置或者是跳出本次循環到指定位置。

label1:
while(true){
    label2:
    while(true){
        continue label1;//結束本次循環,跳轉到外循環繼續循環
        break label1://結束外層循環
    }
}

感慨

Java的基本語法,與其他的語言有相似之處,也有不同之處,但編程本來就是一種實踐的技能,紙上得來終覺淺,絕知代碼要手擼,還是實打實的寫代碼才可以將知識轉化為能力。以及,文章真難寫。

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

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

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

回聲消除中的LMS和NLMS算法與MATLAB實現

  自適應濾波是数字信號處理的核心技術之一,在科學和工業上有着廣泛的應用領域。自適應濾波技術應用廣泛,包括回波抵消、自適應均衡、自適應噪聲抵消和自適應波束形成。回聲對消是當今通信系統中普遍存在的現象。聲回波引起的信號干擾會分散用戶的注意力,降低通信質量。本文重點介紹了LMS和NLMS算法的使用,以減少這種不必要的回聲,從而提高通信質量

關鍵詞:自適應濾波器,自適應算法,回聲消除

1  引言

  當音頻信號在真實環境中產生混響時,就會產生聲學回聲,從而導致原始信號加上信號[1]的衰減、延時圖像。本文將重點研究通信系統中聲學回波的產生。

  自適應濾波器是一種動態濾波器,它不斷地改變其特性以獲得最優的輸出。自適應濾波算法通過改變參數使期望輸出d (n)與實際輸出y (n)之間的差值最小化。該函數稱為自適應算法的代價函數(loss)。圖1显示了自適應回聲抵消系統的框圖。其中,濾波器H(n)表示聲環境的脈衝響應,W(n)表示用來抵消回波信號的自適應濾波器。自適應濾波器的目標是使輸出的y(n)與期望的d(n)(在回聲環境中混響的信號)相等。在每次迭代中,誤差信號e(n)=d (n)-y (n)被反饋回濾波器,濾波器的特性也隨之改變。

自適應回聲消除系統

  自適應濾波器的目標是計算期望信號與自適應濾波器輸出之間的差值e(n)。該誤差信號反饋到自適應濾波器,並通過算法改變其係數,以最小化該差值的函數,即代價函數。在聲回波消除的情況下,自適應濾波器的最優輸出與不需要的回波信號等值。當自適應濾波器輸出等於期望信號時,誤差信號為零。在這種情況下,回顯信號將被完全取消,遠用戶將不會聽到他們的任何原始語音返回給他們。

2. 最小均方(LMS)算法

  最小均方(LMS)算法是由Widrow和Hoff在1959年通過對模式識別的研究首次提出的。由此成為自適應濾波中應用最廣泛的算法之一。LMS算法是一種基於隨機梯度的自適應濾波算法,它利用濾波器權重的梯度來收斂到最優的維納解[2-4]。由於其計算簡單而廣為人知並被廣泛使用。正是這種簡單性使它成為判斷所有其他自適應濾波算法的基準。

  隨着LMS算法的每次迭代,自適應濾波器的濾波抽頭(tap)權值按照如下公式進行更新。

$$公式1:w(n+1)=w(n)2\mu e(n)x(n)$$

  這裏x(n)是延時輸入值的輸入向量,$x(n)=[x_1(n)x_2(n)…x_N(n)]^T=[x(n)x(n-1)…x(n-N+1)]^T$。向量$w(n)=[w_0(n)w_1(n)w_2(n)…w_{N-1}(n)]^T$代表自適應FIR濾波器抽頭(tap)權向量在時刻n的係數。參數μ被稱為步長參數和小正的常數。此步長參數控制更新因子的影響。μ必須選擇一個合適的值LMS算法的性能,如果該值太小自適應濾波器的收斂時間會太長;如果μ太大自適應濾波器變得不穩定,導致其輸出發散[5 – 8]

2.1 LMS算法的實現

LMS算法的每次迭代都需要三個不同的步驟,順序如下:

1. FIR濾波器的輸出y(n)用公式2計算。

$$公式2:y(n)=\sum_{i=0}^{N-1}w(n)x(n-1)=w^T(n)x(n)$$

2. 誤差估計的值按公式3計算。

$$公式3:e(n)=d(n)-y(n)$$

3.更新FIR向量的抽頭tap權值,為下一次迭代做準備,如公式4所示。

$$公式4:w(n+1)=w(n)+2\mu e(n)x(n)$$

  LMS算法在自適應濾波中得到廣泛應用的主要原因是其計算簡單,比其他常用的自適應算法更易於實現。LMS算法每次迭代需要2N加法和2N + 1次乘法(N用於計算輸出y(N)),另一個用於通過向量乘法計算標量[9]

clear;
clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
Hn =[0.8783 -0.5806 0.6537 -0.3223 0.6577 -0.0582 0.2895 -0.2710 0.1278 ...     % ...表示換行的意思
    -0.1508 0.0238 -0.1814 0.2519 -0.0396 0.0423 -0.0152 0.1664 -0.0245 ...
    0.1463 -0.0770 0.1304 -0.0148 0.0054 -0.0381 0.0374 -0.0329 0.0313 ...
    -0.0253 0.0552 -0.0369 0.0479 -0.0073 0.0305 -0.0138 0.0152 -0.0012 ...
    0.0154 -0.0092 0.0177 -0.0161 0.0070 -0.0042 0.0051 -0.0131 0.0059 ...
    -0.0041 0.0077 -0.0034 0.0074 -0.0014 0.0025 -0.0056 0.0028 -0.0005 ...
    0.0033 -0.0000 0.0022 -0.0032 0.0012 -0.0020 0.0017 -0.0022 0.0004 -0.0011 0 0];
Hn=Hn(1:order);
mu=0.5;             % mu表示步長
N=1000;             % 橫坐標1000個採樣點
Loop=150;           % 150次循環
EE_NLMS=zeros(N,1); % 不同步長的初始化誤差
for nn=1:Loop       % epoch=150
    % 權重初始化w
    win_NLMS=zeros(1,order);         % NLMS四種步長測試,四個權重——1
    error_NLMS=zeros(1,N)';     % 初始化誤差
    % 均勻分佈的輸入值
    r=sign(rand(N,1)-0.5);          % shape=(1000,1)的(0,1)均勻分佈-0.5,sign(n)>0=1;<0=-1
    % 輸出:輸入卷積Hn得到 輸出
    output=conv(r,Hn);              % r卷積Hn,output長度=length(u)+length(v)-1
    output=awgn(output,snr,'measured');     % 將白高斯噪聲添加到信號中

    % N=1000,每個採樣點
    for i=order:N         % i=81000
      input=r(i:-1:i-order+1);  % 每次迭代取8個數據進行處理
      e_NLMS = output(i)-win_NLMS*input;
      win_NLMS=win_NLMS+e_NLMS*input'/(input'*input);   % NLMS更新權重
      error_NLMS(i)=error_NLMS(i)+e_NLMS^2;
    end
    
    EE_NLMS=EE_NLMS+error_NLMS;     % 把總誤差相加
end
% 對總誤差求平均值
error_NLMS=EE_NLMS/Loop;

figure;
error_NLMS=10*log10(error_NLMS(order:N));
plot(error_NLMS,'r');       % 紅色
axis tight;                 % 使用緊湊的坐標軸
legend('NLMS算法');           % 圖例
title('NLMS算法誤差曲線');     % 圖標題
xlabel('樣本');                     % x軸標籤
ylabel('誤差/dB');                  % y軸標籤
grid on;                            % 網格線

3 歸一化最小均方(NLMS)算法

  LMS算法的主要缺點之一是每次迭代都有一個固定的步長參數。這需要在開始自適應濾波操作之前了解輸入信號的統計信息。實際上,這是很難實現的。即使我們假設自適應回聲抵消系統的唯一輸入信號是語音,但仍有許多因素如信號輸入功率和振幅會影響其性能[10-12]

  歸一化最小均方算法(NLMS)是LMS算法的擴展,LMS算法通過計算最大步長值來繞過這個問題。步長值的計算公式如下

$$Step\ size = \frac{1}{dot\ product(input\ vector,\ input\ vector)}$$

這個步長與輸入向量x(n)的係數的瞬時值的總期望能量的倒數成正比。輸入樣本的期望能量之和也等於輸入向量與自身的點積,以及輸入向量自相關矩陣的跡R[13-15]。

$$公式5:tr[R]=\sum_{i=0}^{N-1}E[x^2(n-i)]\\ \quad\quad =E[\sum_{i=0}^{N-1}x^2(n-i)]$$

NLMS算法的遞歸公式如式6所示

$$公式6:w(n+1)=w(n)+\frac{1}{x^T(n)x(n)}e(n)x(n)$$

3.1 NLMS算法的實現

  NLMS算法已在Matlab中實現。由於步長參數是根據當前的輸入值來選擇的,因此NLMS算法在未知信號下具有更大的穩定性。該算法具有良好的收斂速度和相對簡單的計算能力,是實時自適應回波抵消系統[16]的理想算法

  由於NLMS是標準LMS算法的擴展,因此NLMS算法的實際實現與LMS算法非常相似。NLMS算法的每次迭代都需要按照以下順序執行這些步驟。

1. 計算了自適應濾波器的輸出

$$公式7:y(n)=\sum_{i=0}^{N-1}w(n)x(n-i)=w^T(n)x(n)$$

2. 誤差信號等於期望信號和濾波器輸出之間的差值。

$$公式8:e(n)=d(n)-y(n)$$

3.計算了輸入向量的步長值。

$$公式9:\mu(n)=\frac{1}{x^T(n)x(n)}$$

4. 濾波器抽頭權重更新,為下一次迭代做準備。

$$公式10:w(n+1)=w(n)+\mu(n)e(n)x(n)$$

NLMS算法的每次迭代都需要3N+1次乘法,僅比標準LMS算法多N次。考慮到所獲得的穩定性和回波衰減增益,這是一個可接受的增加。

clear;
clc;
snr=20;     % 信噪比
order=8;    % 自適應濾波器的階數為8
% Hn是濾波器權重
Hn =[0.8783 -0.5806 0.6537 -0.3223 0.6577 -0.0582 0.2895 -0.2710 0.1278 ...     % ...表示換行的意思
    -0.1508 0.0238 -0.1814 0.2519 -0.0396 0.0423 -0.0152 0.1664 -0.0245 ...
    0.1463 -0.0770 0.1304 -0.0148 0.0054 -0.0381 0.0374 -0.0329 0.0313 ...
    -0.0253 0.0552 -0.0369 0.0479 -0.0073 0.0305 -0.0138 0.0152 -0.0012 ...
    0.0154 -0.0092 0.0177 -0.0161 0.0070 -0.0042 0.0051 -0.0131 0.0059 ...
    -0.0041 0.0077 -0.0034 0.0074 -0.0014 0.0025 -0.0056 0.0028 -0.0005 ...
    0.0033 -0.0000 0.0022 -0.0032 0.0012 -0.0020 0.0017 -0.0022 0.0004 -0.0011 0 0];
Hn=Hn(1:order);
mu=0.5;             % mu表示步長
N=1000;             % 橫坐標1000個採樣點
Loop=150;           % 150次循環
% 不同步長的初始化誤差
EE_LMS = zeros(N,1);
EE_NLMS=zeros(N,1);
for nn=1:Loop       % epoch=150
    win_LMS = zeros(1,order);   % 權重初始化w
    error_LMS=zeros(1,N)';      % 初始化誤差
    % 均勻分佈的語音數據輸入
    r=sign(rand(N,1)-0.5);          % shape=(1000,1)的(0,1)均勻分佈-0.5,sign(n)>0=1;<0=-1
    % 輸出:輸入卷積Hn得到 輸出
    output=conv(r,Hn);              % r卷積Hn,output長度=length(u)+length(v)-1
    output=awgn(output,snr,'measured');     % 真實輸出=將白高斯噪聲添加到信號中

    % N=1000,每個採樣點
    for i=order:N         % i=81000
      input=r(i:-1:i-order+1);  % 每次迭代取8個數據進行處理
      e_LMS = output(i)-win_LMS*input;
      
      mu=0.02;      % 步長
      win_LMS = win_LMS+2*mu*e_LMS*input';
      error_LMS(i)=error_LMS(i)+e_LMS^2;
    end
    % 把總誤差相加
    EE_LMS = EE_LMS+error_LMS;

end
% 對總誤差求平均值
error_LMS = EE_LMS/Loop;

figure;
error1_LMS=10*log10(error_LMS(order:N));
plot(error1_LMS,'b.');  % 藍色
axis tight;         % 使用緊湊的坐標軸
legend('LMS算法');       % 圖例
title('LMS算法誤差曲線');  % 圖標題
xlabel('樣本');                     % x軸標籤
ylabel('誤差/dB');                  % y軸標籤
grid on;                            % 網格線

4 LMS算法的結果

  利用Matlab對LMS算法進行了仿真。圖2显示的是通過麥克風從計算機系統收集到的輸入語音信號。圖3显示了從輸入信號派生出的所需回波信號。圖4显示了自適應濾波器的輸出,它將減少輸入信號的回波信號。圖5显示了由濾波器輸出信號計算出的均方誤差信號。圖6是由回波信號對誤差信號的分割得到的衰減。

  自適應濾波器為1025階FIR濾波器。步長設置為0.02。MSE表明,隨着算法的發展,代價函數的平均值逐漸減小。

5 NLMS算法的結果

  用Matlab對NLMS算法進行了仿真。圖7显示了輸入信號。圖8显示了所需的信號。圖9显示了自適應濾波器輸出。圖10显示了均方誤差。圖11显示了衰減。

  自適應濾波器為1025階FIR濾波器。步長設置為0.1。

 

 NLMS算法在均方誤差和平均衰減方面優於LMS算法,其性能總結如表1所示。

 

6 結論

  由於其簡單性,LMS算法是最流行的自適應算法。然而,LMS算法存在收斂速度慢和數據依賴的問題。

  NLMS算法是LMS算法的一個同樣簡單但更健壯的變體,它在簡單性和性能之間表現出比LMS算法更好的平衡。由於其良好的性能,NLMS在實時應用中得到了廣泛的應用。

7. 參考

文章翻譯自論文《2011_adaptive algorithms for acoustic echo cancellation in speech processing》

[1]. Homana, I.; Topa, M.D.; Kirei, B.S.; “Echo cancelling using adaptive algorithms”, Design and Technology of Electronics Packages, (SIITME) 15th International Symposium., pp. 317-321, Sept.2009.

[2]. Paleologu, C.; Benesty, J.; Grant, S.L.; Osterwise, C.; “Variable step-size NLMS algorithms for echo cancellation” 2009 Conference Record of the forty-third Asilomar Conference on Signals, Systems and Computers., pp. 633-637, Nov 2009.

[3]. Soria, E.; Calpe, J.; Chambers, J.; Martinez, M.; Camps, G.; Guerrero, J.D.M.; “A novel approach to introducing adaptive filters based on the LMS algorithm and its variants”, IEEE Transactions, vol. 47, pp. 127-133, Feb 2008.

[4]. Tandon, A.; Ahmad, M.O.; Swamy, M.N.S.; “An efficient, low-complexity, normalized LMS algorithm for echo cancellation”, IEEE workshop on Circuits and Systems, 2004. NEWCAS 2004, pp. 161-164, June 2004.

[5]. Eneman, K.; Moonen, M.; “Iterated partitioned block frequency-domain adaptive filtering for acoustic echo cancellation,” IEEE Transactions on Speech and Audio Processing, vol. 11, pp. 143-158, March 2003.

[6]. Krishna, E.H.; Raghuram, M.; Madhav, K.V; Reddy, K.A; “Acoustic echo cancellation using a computationally efficient transform domain LMS adaptive filter,” 2010 10th International Conference on Information sciences signal processing and their applications (ISSPA), pp. 409-412, May 2010.

[7]. Lee, K.A.; Gan,W.S; “Improving convergence of the NLMS algorithm using constrained subband updates,” Signal Processing Letters IEEE, vol. 11, pp. 736-739, Sept. 2004.

[8]. S.C. Douglas, “Adaptive Filters Employing Partial Updates,” IEEE Trans.Circuits SYS.II, vol. 44, pp. 209-216, Mar 1997.

[9]. D.L. Duttweiler, “Proportionate Normalized Least Mean Square Adaptation in Echo Cancellers,” IEEE Trans. Speech Audio Processing, vol. 8, pp. 508-518, Sept. 2000.

[10]. E. Soria, J. Calpe, J. Guerrero, M. Martínez, and J. Espí, “An easy demonstration of the optimum value of the adaptation constant in the LMS algorithm,” IEEE Trans. Educ., vol. 41, pp. 83, Feb. 1998.

[11]. D. Morgan and S. Kratzer, “On a class of computationally efficient rapidly converging, generalized NLMS algorithms,” IEEE Signal Processing Lett., vol. 3, pp. 245–247, Aug. 1996.

[12]. G. Egelmeers, P. Sommen, and J. de Boer, “Realization of an acoustic echo canceller on a single DSP,” in Proc. Eur. Signal Processing Conf. (EUSIPCO96), Trieste, Italy, pp. 33–36, Sept. 1996.

[13]. J. Shynk, “Frequency-domain and multirate adaptive filtering,” IEEE Signal Processing Mag., vol. 9, pp. 15– 37, Jan. 1992.

[14]. Ahmed I. Sulyman and Azzedine Zerguine, “Echo Cancellation Using a Variable Step-Size NLMS Algorithm”, Electrical and Computer Engineering Department Queen’s University.

[15]. D. L. Duttweiler, “A twelve-channel digital echo canceller,” IEEE Trans. Commun., vol. 26, no. 5, pp. 647–653, May 1978.

[16]. J. Benesty, H. Rey, L. Rey Vega, and S. Tressens, “A nonparametric VSS NLMS algorithm,” IEEE Signal Process. Lett., vol. 13, pp. 581–584, Oct. 2006.

 

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

【其他文章推薦】

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

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

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

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

※避免吃悶虧無故遭抬價!台中搬家公司免費估價,有契約讓您安心有保障!

工信部第285批新車公示218款新能源入選

根據《中華人民共和國行政許可法》和《國務院對確需保留的行政審批專案設定行政許可的決定》的規定,工信部日前將許可的汽車、摩托車、三輪汽車和低速貨車生產企業及產 品(第285批)予以了公告,共有218款新能源車型入選。進入該公告的新能源汽車可開展生產銷售,但是要獲得補貼,還需再獲得《新能源汽車推廣應用推薦車型目錄》准入。

純電動轎車/乘用車方面,北汽、長城、禦捷馬、卡威、吉利等12款車型入選。

插電式乘用車方面,比亞迪、寶馬、之諾、上汽等7款車型入選。

純電動客車方面,安凱、江淮、安源、北奔、北方、福田、比亞迪、白雲、五菱、陸地方舟、尼歐凱、友誼、青年、海格、開沃、依維柯、飛燕、大通、象牌、野馬、華新、金龍、金旅、宇通、黃河、中通、中植汽車、穗通等28個品牌75款車型入選。

插電式混動客車方面, 安凱、海格、易聖達、金龍、金旅、宇通6個品牌21款車型入選。

新能源專用車方面,福田、北京、大運、黃海、東風、華神、揚子江、東風、福建、環球、藍速、田野、中悅 、卡威、陸地方舟、青年曼、康迪、五菱、暢達、躍進、金龍、凱馬、時風、太行成功、東風、金杯、邢牛、海德、解放、神州、宇通、長帆汽車、迪馬、炫虎等33個品牌103款車型入選。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

中國汽車工業三十強榜單發佈:冠軍上汽集團營收超萬億元

2016年5月26日,中國機械工業聯合會、中國汽車工業協會發佈最新中國汽車工業三十強榜單。上汽集團以2015年1.2萬億元的總營業收入繼續蟬聯冠軍,一汽和東風依然排第2、3位。

榜單中,蓋世汽車盤點出有乘用車公司16家,商用車公司4家,零部件公司7家,摩托車公司3家。本次榜單基於“年度匯總口徑快報”統計2015年營業收入,和上市公司合併財務報表中的營業收入存在計算上的差別,後者抵消了關聯交易部分。這也是為何上汽2015年年報中營收為670,448,223,139.34元,而本榜單中為1.2萬億元的緣故。

對比2015年排名(以2014年營業收入統計),19家企業排名沒有變化,比亞迪和威孚高科新上榜,陝西汽車和金城集團落榜。前十名中,僅長城(今年第9)和重汽(今年第10)互換了名次,其他8家企業都保持了去年的座次。提升最快的是中鼎,從第27提高到第24,上升3位。下降幅度最大的是法士特,從第24滑落至第28。

冠軍上汽集團去年營收總額超過第2名一汽和第3名東風總和。

以下是具體排名

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

【其他文章推薦】

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

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

財政部將公開新能源汽車推廣騙補核查和處理結果

5月28日,財政部發佈聲明稱,關於新能源汽車推廣騙補核查,現場核查已經完成,目前處於會審階段。核查及處理情況,將按資訊公開有關規定及時公開。

中國新能源汽車產業的發展受到政策的強力推動。從2010年開始,我國便實施新能源汽車補貼政策,由於監督機制不完善,騙補隨之愈演愈烈。2016年1月份,工信部、財政部、科技部、發改委聯合啟動對新能源汽車相關情況的專項核查工作,新能源汽車生產企業、運營企業、租賃企業、企事業單位等新能源汽車使用者全部列入核查物件。國務院也把遏制新能源汽車騙補行為作為重點工作之一。

此前,央視曝光了10家涉及騙補的企業,分別是蘇州吉姆西客車製造有限公司、陝西通家汽車股份有限公司、重慶力帆乘用車有限公司、江蘇陸地方舟新能源電動汽車有限公司、奇瑞萬達貴州客車股份有限公司、國宏汽車有限公司、江蘇奧新新能源汽車有限公司、蕪湖寶騏汽車製造有限公司、重慶力帆汽車有限公司,以及金華青年汽車製造有限公司。這些企業的共同特點是,2015年12月單月產量(主要依據是機動車出廠合格證)均超過全年產量的50%。

對於騙補行為,工信部部長苗圩曾公開表示,“局部地區確實存在少部分企業騙補的現象,對於騙補企業,沒補貼的錢不會下發,已補貼的錢一定要扣回。依法進行處置,直至取消這些企業的資質”。

對於騙補的企業到底有哪些?將受到怎樣的處罰?這些問題一直受到業界的關注和猜測。此次,財政部公開發佈新能源汽車推廣核查有關情況的聲明,表示“現場核查已經完成,目前處於會審階段”。聲明特別強調“財政部和部內有關司局至今未接受過媒體採訪,核查及處理情況,將按資訊公開有關規定及時公開”。

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

大眾汽車計畫投資100億歐元新建一座電池工廠

據外媒報導,大眾汽車計畫投資100億歐元新建一座電池工廠,意欲轉型為一家領先的電動汽車製造商,從而使其可以自己掌控電動汽車的命運,而不是單純依賴于亞洲的電池廠商。

在大眾汽車6月22日召開年度股東大會以前,該公司的一個非執行理事會將對首席執行官馬蒂亞斯•穆勒(Matthias Müller)及其下屬一個團隊提出的計畫進行考慮。根據《商報》的報導,這項計畫的規模將需要足夠大到可以支援一個目標,即在未來十年時間裡將純電動汽車(PEV)的銷售量提高至100萬輛。

從投資額來看,大眾汽車計畫建設的這座工廠類似於特斯拉汽車的“超級工廠”(Gigafactry),而該公司所需電池的規模也意味著這將是一座龐大的工廠,但大眾汽車尚未透露更多細節。

據報導,大眾汽車的董事會很可能將會批准這項計畫,而身為該公司大股東之一的德國下薩克森州(Lower Saxony)也將支持該計畫。

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

【其他文章推薦】

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

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?

北汽規劃打造四大海外基地 南非新工廠將投產

據媒體報導,從北汽集團官方獲悉,北汽將斥資50億元在南非建設總裝 廠,該工廠擬於下月開始建設,計畫於2017年11月建成投產。按照規劃,北汽將打造包括南非、伊朗在內的四大海外運營基地並輻射周邊市場,形成四大屬地化產業運營集團。

未來北汽將海外市場拓展劃分了三條線,歸納為“一帶一路一洲 哥倫布航線”,其中“一帶一路”符合大環境下的海外發展趨勢,“一洲”則是“一帶一路”中涉及到的非洲,在此基礎上,北汽增加了“哥倫布航線”沿途的中南美地區。繼續細化,北汽將以“南非、伊朗、東南亞、墨西哥”四大重點專案為引領,建設輻射周邊市場的四大海外運營基地,逐步實現從“旅行者”到“定居者”的角色轉變。

與跨國車企在中國的發展要尋求本地車企進行合作類似,北汽在南非新建的工廠將由北汽集團與南非工業開發公司的合資企業負責運營。新工廠總投資金額達到50億元(7.73億美元),計畫於下月開始建設,有望於2017年11月建成投產,計畫年產能為10萬輛。

據介紹,南非工廠將作為試點,為後續北汽加速海外涉及12個國家的19個KD(散件組裝廠)專案建設積累經驗。北汽集團已於2013年在南非開設了一家小型SKD廠,位於斯普林斯鎮,該廠生產小型麵包計程車,此次在南非斥鉅資打造新工廠並作為試點也就不難理解。

北京汽車國際發展公司擁有五大核心業務,包括自主品牌整車和零部件產品的出口,技術、設備、整車的進口,此外還有產品改裝,境外投資以及國際合作,初期投放海外市場的產品也將以北汽自主品牌為主。這意味著即將建成的南非汽車生產廠將有望投產包括北京紳寶、北京牌和北汽威旺三大產品系列,初步形成對當地市場佈局的同時還將進行他國出口,擴大南非及周邊國家和地區市場份額。

在北汽擴大海外市場佈局後,未來市場銷量將成為企業諸多努力的體現。“十三五”期間北汽制定了“2030”戰略,戰略中指出企業要在2020年實現20萬輛整車出口,完成3個建設,包括國際化運營隊伍建設、合作夥伴隊伍建設、體系能力建設,從而實現品牌高端市場的突破。

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

【其他文章推薦】

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

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

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

大陸寄台灣空運注意事項

大陸海運台灣交貨時間多久?

別翻了,這篇文章就是要讓你入門java多線程!

目錄

就在前幾天,有位讀者朋友私信宜春,說期待出一篇多線程的文章,我當時內心是小鹿亂撞啊….於是這幾天茶不思飯不想,好幾天深夜皆是輾轉反側,兩目深凝,以至於這幾天走起路來格外飄飄然,左搖右晃的,魔鬼般的步伐,一般兩步,走在大馬路中央上差點被打~我承認太誇張了,感覺又要被打~。最終還是君意不可違,答應了這位讀者朋友,從這位讀者朋友的博客頭像可以看的出來,這位朋友絕bi歷經滄桑,對生活無盡的坦然浩對,看透俗世凡塵、世態炎涼、趨炎附勢,擁有着極高的安心恬盪情懷…啥?啥子?這個是系統默認頭像….嗯嗯嗯呃。。。那個那個宜春啥都沒說哈,別把什麼事都扯宜春身上,你們一天天的,我啥都沒說(理直氣壯)…

@

1. 理解線程與進程

由於併發肯定涉及到多線程,因此在進入併發編程主題之前,我們先來了解一下進程和線程的由來,這對後面對併發編程的理解將會有很大的幫助。

進程和線程的對比這一知識點由於過於基礎,正因為過於基礎,所以我們更應該透徹它!我們必須掌握什麼是線程和進程,掌握線程與進程的關係、區別及優缺點 !

1.1、何為進程?

首先我們來看一下進程的概念:

進程:是指一個內存中運行的應用程序,每個進程都有一個獨立的內存空間,一個應用程序可以同時運行多個進程;進程也是程序的一次執行過程,是系統運行程序的基本單位;系統運行一個程序即是一個進程從創建、運行到消亡的過程。

看完之後,是不是感覺很抽象?很懵bi?懵bi就對了,說明你和我智商一樣高….~開個玩笑~

不妨先憋棄上面的概念,放鬆一下大腦,雙擊打開LOL,秒選德馬打野,輸了直接退出遊戲並且保持微笑,然後正襟危坐心平氣和的看宜春寫的博客….

這個時候的你不僅僅是愉快的擼了一把遊戲,而且還親自體驗擼了一把進程…其實在你雙擊打開LOL的時候就已經創建了進程,此話怎講?眾所周知,我們的電腦安裝的軟件比如:LOL、微信、谷歌等等都是存儲在我們的硬盤上的,硬盤上的數據可以說是永久存儲(ORM),當我們雙擊LOL的時候,LOL程序執行就進入了內存中,所有的程序必須進入內存中才能執行,內存屬於臨時存儲(RAM),而進入內存的程序都可以叫做是進程,把LOL程序退出的時候,LOL程序就會退出內存,進程也就隨之銷毀了!因此說各位擼了一把進程也不為過吧。

啥?字太多了,看的不夠明了,不如看圖得勁….額。。。

上面主要是通過抽象的描述了進程,其實進程是可以很直觀的看的到的,我們可以再電腦底部任務欄,右鍵—–>打開任務管理器,可以查看當前任務的進程:

其實,關於線程博主我完全可以一兩句話概括,但是這樣並不負責,畢竟這篇文章標題就是要讓你徹底入門java多線程。如果連進程都理解不好談何徹底理解多線程?

1.2、何為線程?

同樣的,我們先來看線程的概念

線程是進程中的一個執行單位,負責當前進程中程序的執行。一個進程中至少有一個線程,也就是說一個進程可以有多個線程的,而多個線程的進程運用程序就叫做多線程程序

線程的概念稍微好理解很多,但是想更深層次的去理解光靠上面一段文字的概述是完全不夠的!

這不打LOL的過程中,屬實卡的一批,果然花高價998買的6手戴爾筆記本打LOL屬實像極了愛情。這個時候不得不雙擊打開電腦安全管家進行殺毒,果然2500天沒有進行過病毒查殺,我天。。。其實我相信很多人都用過電腦管家或者手機管家之類的安全軟件,我們都很清楚我們開啟病毒查殺之後一般要幾分鐘掃描查殺,這個時候我們是可以讓它後台進行的,我們不會等而是開啟另一個垃圾清理的功能,這個時候我們也不會等而是再去啟動電腦加速功能。等到 這些操作都完成之後果斷退出電腦管家,繼續LOL,果然高價998買的6手戴爾筆記本再怎麼殺毒打LOL還是照樣的卡….

其實清楚線程必然涉及到CPU的相關概念了,將上面文字所描述的用圖片概括,大致為:

1.3、何為多線程?

從上一節中,我們也提到過多線程,所以理解起來應該不難。

多線程就是多個線程同時運行交替運行

單核CPU:交替運行。
多核CPU:同時運行。

其實,多線程程序並不能提高程序的運行速度,但能夠提高程序運行效率,讓CPU的使用率更高。

1.4、何為線程調度優先級?

說起線程調度優先級這個概念,就讓我想到現在我們大部分人投簡歷一樣。如果你的學歷或者工作經驗越高,那麼你的優先級就越高,面試官很大幾率就會讓你去面試但也不是一定只是幾率特別大,如果線程的優先級相同,那麼會隨機選擇一個(線程隨機性)!在我們每個人的電腦中線程是可以設置線程的優先級的,但是生活中沒有優先級(學歷、工作經驗)的孩子就只能靠自己的能力了~媽耶,太真實了…~

線程優先級具有繼承特性比如A線程啟動B線程,則B線程的優先級和A是一樣的。

線程優先級具有隨機性也就是說線程優先級高的不一定每一次都先執行完,只是被執行的可能性更大。

在今後的多線程學習旅遊中我們會使用到getPriority()方法獲取線程的優先級。

1.5、為什麼提倡使用多線程而不是多進程?

線程與進程相似,但線程是一個比進程更小的執行單位,是程序執行的最小單位。一個進程在其執行的過程中可以產生多個線程。與進程不同的是同類的多個線程共享同一塊內存空間和一組系統資源,所以系統在產生一個線程,或是在各個線程之間作切換工作時,負擔要比進程小得多,也正因為如此,線程也被稱為輕量級進程。同時線程是程序執行的最小單位。使用多線程而不是用多進程去進行併發程序的設計,是因為線程間的切換和調度的成本遠遠小於進程。

而使用多線程,多線程會將程序運行方式從串行運行變為併發運行,效率會有很大提高。

2、理解并行和併發

在博主認為併發和并行是兩個非常容易被混淆的概念。為了防止繞暈大家,所以我選擇長話短說!

  1. 併發:一個時間段內同時發生(並不是同時發生)。
  2. 并行:同一時刻發生(真正的同時發生)。

它們都可以表示兩個或者多個任務一起執行,但是偏重點有些不同。

於此同時,我們不妨回顧一下上面所提到過的CPU,並再次理解併發與并行的區別,從而溫故知新 ~我TM簡直是個天才!~

單核CPU:交替運行【併發】
多核CPU:同時運行【并行】

併發給人的感覺是同時運行,那是因為分時交替運行的時間是非常短的!

3、特殊的一個單線程:主線程(Main線程)

我們常說的主線程就是Main線程,它是一個特殊的單線程,話不多說,直接擼碼:

定義一個用於測試的demo類Person

package demo;

public class Person {
   public String name;

   public Person(String name){
       this.name=name;
   }

   public void run(){
       int i=1;
       while (i<5){
           System.out.println(name+i);
           i++;
       }
   }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

編寫Main方法

package demo;

public class MainThreadDemo {
    public static void main(String[] args) {
        Person per=new Person("常威");
        per.run();

        Person Per2=new Person("來福");
        Per2.run();
    }
}

運行結果就已經很顯而易見了,放心我不是靠你們運行結果而是單純的先分析主線程。

運行結果:
    常威1
    常威2
    常威3
    常威4
    來福1
    來福2
    來福3
    來福4

3.1、分析主線程原理

3.2、 單線程的局限性

單線程不僅效率低下,而且存在很大的局限性,惟一的優點就是安全。所以說女孩子長得安全其實也是一種優點,噗哈哈哈…

如何體現出單線程效率低下以及它的局限性呢?其實只要一句代碼即可,還是以上面的單線程Main線程為例:

package demo;

public class MainThreadDemo {
    public static void main(String[] args) {
        Person per=new Person("常威");
        per.run();
        int a=6/0;  //=====================特別注意這行代碼
        Person Per2=new Person("來福");
        Per2.run();
    }
}

試想一下運行結果…

如果對上面的運行結果有問題,或者疑問。那沒錯了,你簡直是個天(小)才(白)!真真的天(小)才(白),很有可能異常機制沒學好,好吧我給你貼出來:

言歸正傳,效率低下何以見得?這是數據少,如果是一億條數據呢,單線程就是一個一個打印。那局限性又何以見得呢?從上面運行結果來看也能看出,只因為一行代碼而導致下面代碼不再執行。已經很明顯了。

4、 創建多線程的四種方式

說是說創建多線程有四種方式,但考慮到是入門文章還是主要寫入門的兩種方式,剩下的兩個暫時忽略。忽略的兩種方法有:實現Callable接口通過FutureTask包裝器來創建Thread線程、使用ExecutorServiceCallableFuture實現有返回結果的線程。現在可能對於入門的童鞋來說是接收不了的,以後再去了解也不晚!

4.1、繼承Thread類

Java使用java.lang.Thread類代表線程,所有的線程對象都必須是Thread類或其子類的實例。每個線程的作用是完成一定的任務,實際上就是執行一段程序流即一段順序執行的代碼。Java使用線程執行體來代表這段程序流。

Java中通過繼承Thread類來創建啟動多線程的步驟如下:

  1. 定義Thread類的子類,並重寫該類的run()方法,該run()方法的方法體就代表了線程需要完成的任務,因此把run()方法稱為線程執行體。
  2. 創建Thread子類的實例,即創建了線程對象
  3. 調用線程對象的start()方法來啟動該線程

代碼如下:

測試類:

public class Demo01 {
    public static void main(String[] args) {
        //創建自定義線程對象
        MyThread mt = new MyThread("新的線程!");
        //開啟新線程
        mt.start();
        //在主方法中執行for循環
        for (int i = 0; i < 10; i++) {
            System.out.println("main線程!"+i);
        }
    }
}

自定義線程類:

public class MyThread extends Thread {
    //定義指定線程名稱的構造方法
    public MyThread(String name) {
        //調用父類的String參數的構造方法,指定線程的名稱
        super(name);
    }
    /**
     * 重寫run方法,完成該線程執行的邏輯
     */
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            System.out.println(getName()+":正在執行!"+i);
        }
    }
}

Thread類本質上是實現了Runnable接口的一個實例,代表一個線程的實例。啟動線程的唯一方法就是通過Thread類的start()實例方法。start()方法是一個native方法,它將啟動一個新線程,並執行run()方法。這種方式實現多線程很簡單,通過自己的類直接extend Thread,並複寫run()方法,就可以啟動新線程並執行自己定義的run()方法。

4.2、實現Runnable接口

如果自己的類已經繼承另一個類,就無法直接繼承Thread,此時,可以實現一個Runnable接口來創建線程,顯然實現Runnable接口方式創建線程的優勢就很明顯了。

直接擼碼:

自定義一個類實現Runnable接口,並重寫接口中的run()方法,併為run方法添加要執行的代碼方法。

public class RunableDemo implements Runnable{

    @Override
    public void run() {
        int a = 1;
        while (a<20){
            System.out.println(Thread.currentThread().getName()+ a);//Thread.currentThread().getName()為獲取當前線程的名字
            a++;
        }
    }
}

編寫Main方法

為了啟動自定義類RunableDemo ,需要首先實例化一個Thread,並傳入RunableDemo 實例

public class MainThreadDemo {

    public static void main(String[] args) {
        RunableDemo runn=new RunableDemo();
        
        //實例化一個Thread並傳入自己的RunableDemo 實例
        Thread thread=new Thread(runn);
        thread.start();

        int a = 1;
        while (a<20){
            //Thread.currentThread().getName()為獲取當前線程的名字
            System.out.println(Thread.currentThread().getName()+ a);
            a++;
        }
    }
}

運行結果:

main1
main2
main3
Thread-01
Thread-02
Thread-03
Thread-04
Thread-05
Thread-06
....

其實多運行幾遍,你會方法每次運行的結果順序都不一樣,這主要是由於多線程會去搶佔CPU的資源,誰搶到了誰就執行,而Main和Thread兩個線程一直在爭搶。

實際上,當傳入一個Runnable target(目標)參數給Thread后,Threadrun()方法就會調用target.run(),參考JDK源代碼:

public void run() {  
  if (target != null) {  
   target.run();  
  }  
}  

4.3、兩種入門級創建線程的區別

採用繼承Thread類方式:

(1)優點:編寫簡單,如果需要訪問當前線程,無需使用Thread.currentThread()方法,直接使用this,即可獲得當前線程。
(2)缺點:因為線程類已經繼承了Thread類,所以不能再繼承其他的父類。

採用實現Runnable接口方式:

(1)優點:線程類只是實現了Runable接口,還可以繼承其他的類。在這種方式下,可以多個線程共享同一個目標對象,所以非常適合多個相
同線程來處理同一份資源的情況,從而可以將CPU代碼和數據分開,形成清晰的模型,較好地體現了面向對象的思想。
(2)缺點:編程稍微複雜,如果需要訪問當前線程,必須使用Thread.currentThread()方法。

小結:
如果一個類繼承Thread,則不適合資源共享。但是如果實現了Runable接口的話,則很容易的實現資源共享。

實現Runnable接口比繼承Thread類的優勢:

1.適合多個相同代碼的線程去處理同一個資源。

2.可以避免java中單繼承的限制。

3.增加代碼的健壯性,實現解耦。代碼可以被多個線程共享,代碼和數據獨立。

4.線程池中只能放入實現Runnable或Callable類線程,不能放入繼承Thread的類【線程池概念之後會慢慢涉及】

所以,如果選擇哪種方式,盡量選擇實現Runnable接口

其實學到後面的線程池,你會發現上面兩種創建線程的方法實際上很少使用,一般都是用線程池的方式比較多一點。使用線程池的方式也是最推薦的一種方式,另外,《阿里巴巴Java開發手冊》在第一章第六節併發處理這一部分也強調到“線程資源必須通過線程池提供,不允許在應用中自行显示創建線程”。不過處於入門階段的童鞋博主還是強烈建議一步一個腳印比較好!

5、使用匿名內部類方式創建線程

談起匿名內部類,可能很多小白是比較陌生的,畢竟開發中使用的還是比較少,但是同樣是非常重要的一個知識!於此同時我就貼出關於匿名內部類的文章如果小白童鞋能看懂下面這個代碼,真的你不需要看那篇文章了,你T喵的簡直是個天才!

package AnonymousInner;

public class NiMingInnerClassThread {
    public static void main(String[] args) {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                for (int i = 0; i<5;i++){
                    System.out.println("熊孩子:"+i);
                }
            }
        };
        new Thread(r).start();
        for (int i = 0; i < 5 ; i++){
            System.out.println("傻狍子:"+i);
        }
    }
}

小白童鞋還愣着幹啥呀趕緊去補補…

6、線程安全問題

線程安全問題主要是共享資源競爭的問題,也就是在多個線程情況下,一個或多個線程同時搶佔同一資源導致出現的一些不必要的問題,最典型的例子就是火車四個窗口售票問題了,這裏就不再舉售票例子了,已經爛大街了,這裏就簡單實現一個線程安全問題代碼….

實現Runnable接口方式為例,主要實現過程是:實例化三個Thread,並傳入同一個RunableDemo 實例作為參數,最後開啟三條相同參數的線程,代碼如下:

public class RunableDemo implements Runnable{
    public int a = 100;//線程共享數據
    
    @Override
    public void run() {
        while (a>0){
            System.out.println("線程"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
    }
}
public class MainThreadDemo {

    public static void main(String[] args) {
        RunableDemo runn=new RunableDemo();
        
        Thread thread1=new Thread(runn);
        Thread thread2=new Thread(runn);
        Thread thread3=new Thread(runn);
        thread1.start();
        thread2.start();
        thread3.start();
        }
 }

運行結果:

Thread-0==100
Thread-0==99
Thread-1==100
Thread-1==97
Thread-1==96
Thread-1==95
Thread-2==98
...

根據結果可以看出,確實是三條線程(Thread-0、1、2)在執行,安全問題就出在線程會出現相同的結果比如上面的100就出現了兩次,如果循環條件更改一下可能也會出現負數的情況。這種情況該怎麼解決呢?這個時候就需要線程同步了!

7、解決線程安全問題:線程同步

實際上,線程安全問題的解決方法有三種:

1、同步代碼塊
2、同步方法
3、鎖機制

7.1、 synchronized同步代碼塊

第一種方法:同步代碼塊

格式:

synchronized(鎖對象) {
可能會出現線程安全問題的代碼(訪問共享數據的代碼)
}

使用同步代碼塊特別注意:
1、通過代碼塊的鎖對象,可以是任意對象
2、必須保證多個線程使用的鎖對象必須是同一個
3、鎖對象的作用是把同步代碼快鎖住,只允許一個線程在同步代碼塊執行

還是以上麵線程安全問題為例子,使用同步代碼塊舉例:

public class RunableDemo implements Runnable{
    public int a = 100;//線程共享數據

    Object object=new Object(); //事先準備好一個鎖對象

    @Override
    public void run() {
        synchronized (object){  //使用同步代碼塊
        while (a>0){
            System.out.println("線程"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
        }
    }
}

Main方法沒有任何改動,運行一下結果是絕對沒問題的,數據都是正確的沒有出現重複情況這一出,各位可以自己嘗試一下!

同步代碼塊的原理:

使用了一個鎖對象,叫同步鎖,對象鎖,也叫同步監視器,當開啟多個線程的時候,多個線程就開始搶奪CPU的執行權,比如現在t0線程首先的到執行,就會開始執行run方法,遇到同步代碼快,首先檢查是否有鎖對象,發現有,則獲取該鎖對象,執行同步代碼塊中的代碼。之後當CUP切換線程時,比如t1得到執行,也開始執行run方法,但是遇到同步代碼塊檢查是否有鎖對象時發現沒有鎖對象,t1便被阻塞,等待t0執行完畢同步代碼塊,釋放鎖對象,t1才可以獲取從而進入同步代碼塊執行。
同步中的線程,沒有執行完畢是不會釋放鎖的,這樣便實現了線程對臨界區的互斥訪問,保證了共享數據安全。
缺點:頻繁的獲取釋放鎖對象,降低程序效率

7.2、同步方法

使用步驟:

1、把訪問了共享數據的代碼抽取出來,放到一個方法中
2、在該方法上添加 synchronized 修飾符

格式:

修飾符 synchronized 返回值類型 方法名稱(參數列表) {
  方法體...
}

代碼示例:

public class RunableDemo implements Runnable{
    public int a = 100;//線程共享數據

    @Override
    public void run() {
        while (true){
            sell(); //調用下面的sell方法
        }
    }
    
    //訪問了共享數據的代碼抽取出來,放到一個方法sell中 
    public synchronized void sell(){
        while (a>0){
            System.out.println("線程"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
    }
}

同步方法的也是一樣鎖住同步的代碼,但是鎖對象的是Runable實現類對象,也就是this,誰調用方法,就是誰。

說到同步方法,就不得不說一下靜態同步方法,顧名思義,就是在同步方法上加上static,靜態的同步方法,添加一個靜態static修飾符,此時鎖對象就不是this了,靜態同步方法的鎖對象是本類的class屬性,class文件對象(反射)

public class RunableDemo implements Runnable{
    public static int a = 100;//線程共享數據     =====此時共享數據也要加上static

    @Override
    public void run() {
        while (true){
            sell();
        }
    }

    public static synchronized void sell(){  //注意添加了static關鍵字
        while (a>0){
            System.out.println("線程"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
    }
}

使用靜態同步方法時,此時共享數據也要加上static,因為static成員才能訪問static成員,如果對static關鍵字不是他別理解的可以補補了,放心,博主有信心讓你有所收穫,會讓你重新認識到static的魅力:

當然靜態同步方法了解即可!

7.3、Lock鎖

Lock接口位於java.util.concurrent.locks.Lock它是JDK1.5之後出現的,Lock接口中的方法:

void lock(): 獲取鎖

 

void unlock(): 釋放鎖

Lock接口的一個實現類java.util.concurrent.locks.ReentrantLock implements Lock接口

使用方法:
1、在Runable實現類的成員變量創建一個ReentrantLock對象
2、在可能產生線程安全問題的代碼該對象調用lock方法獲取鎖
3、在可能產生線程安全問題的代碼該對象調用unlock方法釋放鎖

代碼示例:

import java.util.concurrent.locks.ReentrantLock;

public class RunableDemo implements Runnable{
    public static int a = 100;//線程共享數據

    //1、在Runable實現類的成員變量創建一個ReentrantLock對象============
    ReentrantLock reentrantLock = new ReentrantLock();

    @Override
    public void run() {
        // 2、在可能產生線程安全問題的代碼前該對象調用lock方法獲取鎖=======
        reentrantLock.lock();
        while (a>0){
            System.out.println("線程"+Thread.currentThread().getName()+"執行到"+ a);
            a--;
        }
        // 3、在可能產生線程安全問題的代碼后該對象調用unlock方法獲取鎖======
        reentrantLock.unlock();
    }

}

當然更安全的寫法是,在線程安全問題代碼中try...catchy,最後在finally語句中添加reentrantLock.unlock();,這樣方為上上策!

7.4、三種方法小結

第一種
synchronized 同步代碼塊:可以是任意的對象必須保證多個線程使用的鎖對象是同一個

 

第二種
synchronized 同步方法: 鎖對象是this,誰調用鎖對象就是誰

 

synchronized 靜態同步方法: 鎖對象是其class對象,該對象可以用this.getClass()方法獲取,也可以使用當前類名.class 表示。【了解即可】

 

第三種
Look鎖方法:該方法提供的方法遠遠多於synchronized方式,主要在Runable實現類的成員變量創建一個ReentrantLock對象,並使用該對象調用lock方法獲取鎖以及unlock方法釋放鎖!

8、線程常用方法

8.1、Thread類

  Thread():用於構造一個新的Thread。

  Thread(Runnable target):用於構造一個新的Thread,該線程使用了指定target的run方法。

  Thread(ThreadGroup group,Runnable target):用於在指定的線程組中構造一個新的Thread,該

  線程使用了指定target的run方法。

  currentThread():獲得當前運行線程的對象引用。

  interrupt():將當前線程置為中斷狀態。

  sleep(long millis):使當前運行的線程進入睡眠狀態,睡眠時間至少為指定毫秒數。

  join():等待這個線程結束,即在一個線程中調用other.join(),將等待other線程結束后才繼續本線程。

  yield():當前執行的線程讓出CPU的使用權,從運行狀態進入就緒狀態,讓其他就緒線程執行。

8.2、Object類

  wait():讓當前線程進入等待阻塞狀態,直到其他線程調用了此對象的notify()或notifyAll()方法后,當前線程才被喚醒進入就緒狀態。

  notify():喚醒在此對象監控器(鎖對象)上等待的單個線程。

  notifyAll():喚醒在此對象監控器(鎖對象)上等待的所有線程。

注意:wait()、notify()、notifyAll()都依賴於同步鎖,而同步鎖是對象持有的,且每個對象只有一個,所以這些方法定義在Object類中,而不是Thread類中。

8.3、yield()、sleep()、wait()比較

   wait():讓線程從運行狀態進入等待阻塞狀態,並且會釋放它所持有的同步鎖。

   yield():讓線程從運行狀態進入就緒狀態,不會釋放它鎖持有的同步鎖。

   sleep():讓線程從運行狀態進入阻塞狀態,不會釋放它鎖持有的同步鎖。

9、線程的狀態

以上只是簡單的一個線程狀態圖,其實線程狀態博大精深,要講清楚還是要一大篇文筆,作為入門文章先了解一下吧,之後的併發編程文章將再講述吧!

如果想要去深入了解一下的話也是可以的:

10、線程池

在java中只要說到池,基本都是一個套路,啥數據庫連接池、jdbc連接池等,思想基本上就是:一個容納多個要使用資源的容器,其中的資源可以反覆使用,省去了頻繁創建線程對象的操作,無需反覆創建資源而消耗過多資源。

10.1、線程池概述

線程池其實就是一個容納多個線程的容器,其中的線程可以反覆使用,省去了頻繁創建線程對象的操作,無需反覆創建線程而消耗過多資源。

合理利用線程池能夠帶來三個好處:

  1. 降低資源消耗。減少了創建和銷毀線程的次數,每個工作線程都可以被重複利用,可執行多個任務。
  2. 提高響應速度。當任務到達時,任務可以不需要的等到線程創建就能立即執行。
  3. 提高線程的可管理性。可以根據系統的承受能力,調整線程池中工作線線程的數目,防止因為消耗過多的內存,而把服務器累趴下(每個線程需要大約1MB內存,線程開的越多,消耗的內存也就越大,最後死機)。

10.2、 線程池的使用

Java裏面線程池的最頂級接口是java.util.concurrent.Executor,但是嚴格意義上講Executor並不是一個線程池,而只是一個執行線程的工具。真正的線程池接口是java.util.concurrent.ExecutorService

要配置一個線程池是比較複雜的,尤其是對於線程池的原理不是很清楚的情況下,很有可能配置的線程池不是較優的,因此在java.util.concurrent.Executors線程工廠類裏面提供了一些靜態工廠,生成一些常用的線程池。官方建議使用Executors工程類來創建線程池對象。

Executors類中有個創建線程池的方法如下:

  • public static ExecutorService newFixedThreadPool(int nThreads):返回線程池對象。(創建的是有界線程池,也就是池中的線程個數可以指定最大數量)

獲取到了一個線程池ExecutorService 對象,那麼怎麼使用呢,在這裏定義了一個使用線程池對象的方法如下:

  • public Future<?> submit(Runnable task):獲取線程池中的某一個線程對象,並執行

Future接口:用來記錄線程任務執行完畢后產生的結果。線程池創建與使用。

使用線程池中線程對象的步驟:

  1. 創建線程池對象。
  2. 創建Runnable接口子類對象。(task)
  3. 提交Runnable接口子類對象。(take task)
  4. 關閉線程池(一般不操作這一步)。

Runnable實現類代碼:

public class MyRunnable implements Runnable {
    @Override
    public void run() {
        System.out.println("我要一個游泳教練");
        try {
            Thread.sleep(2000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("教練來了: " + Thread.currentThread().getName());
        System.out.println("教我游泳,教會後,教練又回到了游泳池");
    }
}

線程池測試類:

public class ThreadPoolDemo {
    public static void main(String[] args) {
        // 創建線程池對象
        ExecutorService service = Executors.newFixedThreadPool(2);//包含2個線程對象
        // 創建Runnable實例對象
        MyRunnable r = new MyRunnable();

        //自己創建線程對象的方式
        // Thread t = new Thread(r);
        // t.start(); ---> 調用MyRunnable中的run()

        // 從線程池中獲取線程對象,然後調用MyRunnable中的run()
        service.submit(r);
        // 再獲取個線程對象,調用MyRunnable中的run()
        service.submit(r);
        service.submit(r);
        // 注意:submit方法調用結束后,程序並不終止,是因為線程池控制了線程的關閉。
        // 將使用完的線程又歸還到了線程池中
        // 關閉線程池
        //service.shutdown();
    }
}

以上只是簡單的使用線程池,僅僅是入門階段!道阻且長,路還很長….

到這裏,本文章入門暫時告一段落,以後有時間盡量抽空更新….

如果本文章對你有幫助,哪怕是一點點,請點個讚唄,謝謝你~

歡迎各位關注我的公眾號,一起探討技術,嚮往技術,追求技術…說好了來了就是盆友喔…

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

【其他文章推薦】

※帶您來了解什麼是 USB CONNECTOR  ?

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

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

※綠能、環保無空污,成為電動車最新代名詞,目前市場使用率逐漸普及化

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

※試算大陸海運運費!

三、netcore跨平台之 Linux配置nginx負載均衡

前面兩章講了netcore在linux上部署以及配置nginx,並讓nginx代理webapi。

這一章主要講如何配置負載均衡,有些步驟在前兩章講的很詳細了,所以這一章我就不會一個個截圖了。

因為本人只有一個服務器。所以我會在同一台服務器上部署兩套差不多的實例。

同樣的代碼,我們在Program.cs進行了修改,如圖所示:

這裏我把原來的端口6666改成了8888

 

 然後你可以改一改你的接口部分的代碼,便於讓你更好的看到效果。

這裏把value1和value2改成value3和value4,這裡是為了看到測試效果,在實際的開發中這裏不用改。

 

 然後發布和上傳到服務器,如何發布和上傳,我在第一章有講到:https://www.cnblogs.com/dengbo/p/11878766.html

注意的是你同樣的地方新建一個新的目錄保存你新上傳的程序,netcore是我第一章建立的,netcore1是新建的,

你把你新的發布包放在netcore即可。如圖:

上傳結束后,在這個目錄中運行你的程序,輸入下面的命令

dotnet WebApiTest.dll   --server.urls "http://*:8888"

如圖所示

 

 然後去看看你的接口是否正常

 

 

好了,這裏的準備工作完成了,下面我們進入到nginx的配置的目錄中

輸入下面的命令:

cd /usr/local/nginx/conf

然後對文件進行編輯

vim nginx.conf

 

 我們需要在這裏修改一下配置。

在如圖的server的平級添加如下的代碼

upstream NgWebApi {
                server localhost:6666;
                server localhost:8888;
    }

上面的 NgWebApi是隨意寫的名稱,不要糾結這裏。

然後在修改 proxy_pass後面的內容:

proxy_pass http://NgWebApi;

最終的結果如下:

 

 這樣你就修改完成,輸入:wq退出並保存即可。

最後檢查並重啟nginx

/usr/local/nginx/sbin/nginx -t
/usr/local/nginx/sbin/nginx -s reload

最後不要忘記把你的8888端口的webapi啟動一下。

這裏我務必要提醒你,請進入到你的程序的目錄中執行這段代碼,

cd /root/netcore1
dotnet WebApiTest.dll   --server.urls "http://*:8888"

啟動如下:

 

 

 好了,配置結束了,下面我們來測試下

 

還是昨天的那個網站進行測試   https://www.sojson.com/httpRequest/

 

 

 

多次發送請求會出現下面的響應

 

 

看到上面兩個請求,就說明你配置成功了,是不是很簡單。

上面這種配置,系統會採用默認的輪詢訪問不同的端口,nginx作為強大的反向代理,強大的遠遠不止這裏

下面簡單講講分發策略。

1)、輪詢 ——輪流處理請求(這是系統默認的)

      每個請求按時間順序逐一分配到不同的應用服務器,如果應用服務器down掉,自動剔除它,剩下的繼續輪詢,如果您的服務器都差不多,建議這個。 

2)、權重 ——誰的設置的大,誰就承擔大部分的請求

      通過配置權重,指定輪詢幾率,權重和訪問比率成正比,用於應用服務器性能不均的情況,有時候你買的服務器可能參差不齊,有的性能強大

    有的一般,你可以通過設置權重,把服務器性能強大權重設置大一點,這樣可以合理分配壓力。 

3)ip_哈希算法

      每一次的請求按訪問iphash結果分配,這樣每個訪客固定訪問一個應用服務器,可以解決session共享的問題。

 

 

關於權重的策略,如下圖示的 你只要加一個  weight=6 即可這裏不一定是6,是整數都行。

 

 

 然後保存即可

這裏不要忘記重啟nginx,以及運行8888端口的程序了,如果你不會,可以看前面的部分

最後我們看看效果

結果和上面的測試結果差不多,唯一不同的是出現下面這個結果的次數要大於另外一個的。

 

 

到這裏就結束了,感謝觀看。

 

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

【其他文章推薦】

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

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

※想要讓你的商品成為最夯、最多人討論的話題?網頁設計公司讓你強力曝光

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

※專營大陸快遞台灣服務

台灣快遞大陸的貨運公司有哪些呢?