跳到主要內容

系統化學習多線程(一)

大綱



-------------------------學前必讀----------------------------------


學習不能快速成功,但一定可以快速入門
整體課程思路:
1.實踐為主,理論化偏少
2.課程筆記有完整的案例和代碼,(為了學習效率)再開始之前我會簡單粗暴的介紹知識點案例思路,
有基礎的同學聽了之後可以直接結合筆記寫代碼,
如果沒聽懂再向下看視頻,我會手把手編寫代碼和演示測試結果;
3.重要提示,學編程和學游泳一樣,多實踐學習效率才高,理解才透徹;
4.編碼功底差的建議每個案例代碼寫三遍,至於為什麼...<<賣油翁>>...老祖宗的智慧


-------------------------------------------------------------------------


 1.線程


1.1.什麼是線程


線程(英語:thread)是操作系統能夠進行運算調度的最小單位。它被包含在進程之中,是進程中的實際運作單位。一條線程指的是進程中一個單一順序的控制流,一個進程中可以併發多個線程,每條線程并行執行不同的任務。在Unix System V及SunOS中也被稱為輕量進程(lightweight processes),但輕量進程更多指內核線程(kernel thread),而把用戶線程(user thread)稱為線程。(來自百度百科)


一個進程可以有很多線程,每條線程并行執行不同的任務。


1.2.多線程hello word


需求:模擬在計算上一邊聽歌一邊打遊戲


三種實現方案如下:

TestDemo
 1 package com.wfd360.thread;
2
3 import com.wfd360.thread.demo01.GameRunnable;
4 import com.wfd360.thread.demo01.MusicRunnable;
5 import com.wfd360.thread.demo02.GameThread;
6 import com.wfd360.thread.demo02.MusicThread;
7 import org.junit.Test;
8
9 /**
10 * @author 姿勢帝-博客園
11 * @address https://www.cnblogs.com/newAndHui/
12 * @WeChat 851298348
13 * @create 05/03 5:27
14 * @description 需求分析:
15 * 1.模擬一邊打遊戲一邊聽音樂,在控制台打印輸出模擬
16 * 2.把兩個業務封裝成獨立的線程,實現接口Runnable或繼承Thread,通過看源碼你會發現Thread類實現了接口Runnable,使用本質上這兩種方法時一樣的。
17 * 3.Thread類提供兩個方法,線程主題方法run,啟動線程方法start
18 */
19 public class TestDemo {
20 /**
21 * 方式1:實現接口Runnable
22 */
23 @Test
24 public void testRunnable() throws InterruptedException {
25 System.out.println("-------test start-------");
26 // 實例對象
27 MusicRunnable music = new MusicRunnable();
28 GameRunnable game = new GameRunnable();
29 // 創建線程
30 Thread musicThread = new Thread(music);
31 Thread gameThread = new Thread(game);
32 // 啟動線程
33 musicThread.start();
34 gameThread.start();
35 System.out.println("--------等待其他線程執行--------------");
36 Thread.sleep(5 * 1000);
37 System.out.println("-------test end-------");
38 }
39
40 /**
41 * 方式2:繼承Thread
42 */
43 @Test
44 public void testThread() throws InterruptedException {
45 System.out.println("-------test start-------");
46 // 創建線程
47 MusicThread musicThread = new MusicThread();
48 GameThread gameThread = new GameThread();
49 // 啟動線程
50 musicThread.start();
51 gameThread.start();
52 System.out.println("--------等待其他線程執行--------------");
53 Thread.sleep(5 * 1000);
54 System.out.println("-------test end-------");
55 }
56
57 /**
58 * 方式3:簡寫,這種寫法一般我們在做模擬測試的使用,在正式代碼中建議不使用,可讀性較差
59 */
60 @Test
61 public void testThreadSimple() throws InterruptedException {
62 System.out.println("-------test start-------");
63 // 創建線程
64 Thread musicThread = new Thread(() -> {
65 for (int i = 0; i < 100; i++) {
66 System.out.println("=======聽音樂中============" + i);
67 }
68 });
69 Thread gameThread = new Thread(() -> {
70 for (int i = 0; i < 100; i++) {
71 System.out.println("=======打遊戲中============" + i);
72 }
73 });
74 // 啟動線程
75 musicThread.start();
76 gameThread.start();
77 System.out.println("--------等待其他線程執行--------------");
78 Thread.sleep(5 * 1000);
79 System.out.println("-------test end-------");
80 }
81 }



 實現接口Runnable


 1 package com.wfd360.thread.demo01;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/03 5:31
8 * @description
9 */
10 public class GameRunnable implements Runnable {
11 @Override
12 public void run() {
13 for (int i = 0; i < 100; i++) {
14 System.out.println("=======打遊戲中============" + i);
15 }
16 }
17 }

GameRunnable
 1 package com.wfd360.thread.demo01;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/03 5:29
8 * @description
9 */
10 public class MusicRunnable implements Runnable {
11 @Override
12 public void run() {
13 for (int i = 0; i < 100; i++) {
14 System.out.println("=======聽音樂中============"+i);
15 }
16 }
17 }

MusicRunnable

 繼承Thread


 1 package com.wfd360.thread.demo02;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/03 6:00
8 * @description
9 */
10 public class GameThread extends Thread {
11 @Override
12 public void run() {
13 for (int i = 0; i < 100; i++) {
14 System.out.println("-------遊戲中----------"+i);
15 }
16 }
17 }

GameThread GameThread
 1 package com.wfd360.thread.demo02;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/03 6:00
8 * @description
9 */
10 public class MusicThread extends Thread {
11 @Override
12 public void run() {
13 for (int i = 0; i < 100; i++) {
14 System.out.println("-------音樂中----------" + i);
15 }
16 }
17 }



 總結


啟動線程兩種方式:


    1.通過繼承Thread類


    2.實現Runnable接口


 使用哪種方式更好?


區別: 


一個類如果繼承了其他類,就無法在繼承Thread類,在Java中,一個類只能繼承一個類,而一個類如果實現了一個接口,還可以實現其他接口,接口是可以多實現的,所以說


Runable的擴展性更強,但是繼承的方式更簡單,個人建議一般情況下使用Thread;


實現接口Runnable或繼承Thread,通過看源碼你會發現Thread類實現了接口Runnable,使用本質上這兩種方法是一樣的

啟動線程流程:


    創建啟動線程的方式一:繼承Thread類


       1.將業務方法封裝成線程對象,自定義類t extends Thread類; 


       2.覆寫run方法: 覆寫第一步中的run方法;


       3.創建自定義對象t


       4.啟動線程 t.start();


   創建啟動線程方式二:實現Runnable接口


      1.將業務方法封裝成線程對象,自定義類t implements Runnable接口;


      2.實現第一步中的run方法


      3.創建自定義對象t


      4.啟動線程 new Thread(t).start();


1.3.對主線程與創建線程執行順序的理解


問題:
直接寫一個簡單的HelloWorld 程序,有沒有線程?
==>有一個主線程,在垃圾回收的時候,有gc 線程。


 1 package com.wfd360.thread;
2
3 import org.junit.Test;
4
5 /**
6 * @author 姿勢帝-博客園
7 * @address https://www.cnblogs.com/newAndHui/
8 * @WeChat 851298348
9 * @create 05/04 11:09
10 * @description <p>
11 * 問題:
12 * 直接寫一個簡單的HelloWorld 程序,有沒有線程?
13 * ==>有一個主線程,在垃圾回收的時候,有gc 線程。
14 * 結論:一旦線程啟動起來之後就是獨立的,和創建環境沒有關係;
15 * 啟動線程不能直接調用run方法,必須調用start方法;
16 * </p>
17 */
18 public class TestDemo02 {
19 /**
20 * 如果把創建線程放在循環語句的 下 面,會交替出現嗎
21 * ==>否,因為主線程執行完成后才會啟動hello線程
22 *
23 * @throws Exception
24 */
25 @Test
26 public void test1() throws Exception {
27 System.out.println("---test start-------");
28 // 執行主線程
29 for (int i = 0; i < 100; i++) {
30 System.out.println("-----test1--------" + i);
31 }
32 // 啟動hello線程
33 new HelloThread().start();
34 System.out.println("=======等待執行完成===========");
35 Thread.sleep(5 * 1000);
36 System.out.println("---test end-------");
37 }
38
39 /**
40 * 如果把創建線程放在循環語句的 上 面,會交替出現嗎
41 * ==>可能會,可能不會,可能出現for循環完之後,線程還沒有啟動完;
42 *
43 * @throws Exception
44 */
45 @Test
46 public void test2() throws Exception {
47 System.out.println("---test start-------");
48 // 啟動hello線程
49 new HelloThread().start();
50 // 執行主線程
51 for (int i = 0; i < 100; i++) {
52 System.out.println("-----test1--------" + i);
53 }
54 System.out.println("=======等待執行完成===========");
55 Thread.sleep(5 * 1000);
56 System.out.println("---test end-------");
57 }
58
59 /**
60 * 採用內部類的方式定義一個hello線程對象
61 */
62 class HelloThread extends Thread {
63 @Override
64 public void run() {
65 for (int i = 0; i < 100; i++) {
66 System.out.println("-----HelloThread--------" + i);
67 }
68 }
69 }
70 }

TestDemo02

結論:一旦線程啟動起來之後就是獨立的,和創建環境沒有關係;
啟動線程不能直接調用run方法,必須調用start方法;


 1.4.對sleep方法的理解


package com.wfd360.thread;

/**
*
@author 姿勢帝-博客園
* @address
https://www.cnblogs.com/newAndHui/
* @WeChat 851298348
* @create 05/04 11:34
* @description <p>
* Thread類的方法:
* static void sleep(long millis) 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行);
* </p>
*/
public class TestSleep {
/**
* 做一個簡易倒計時,10秒鐘,控制台每一秒輸出一個数字,如10,9,8,7.....0
*/
public static void main(String[] args) throws Exception {
System.out.println(
"---test start-------");
for (int i = 10; i >= 0; i--) {
Thread.sleep(
1 * 1000);
System.out.println(i);
}
System.out.println(
"---test end-------");
}
}

1.5.線程名稱的設置與獲取


繼承方式


簡單需求:使用多線程模擬多窗口售票


 1 package com.wfd360.thread.demo03Ticket;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/04 11:55
8 * @description <p>
9 * 模擬多線程售票
10 * </p>
11 */
12 public class TicketThread extends Thread {
13 // 假定票總是100張
14 private static Integer num = 100;
15
16 @Override
17 public void run() {
18 // 只要有票就一直售票
19 while (num > 0) {
20 System.out.println("正在出售第" + num + "張票");
21 --num;
22 }
23 System.out.println("===售票結束===");
24 }
25 }

TicketThread

test


/**
* 測試模擬三個窗口售票
*
@throws InterruptedException
*/
@Test
public void testTicketThread() throws InterruptedException {
System.out.println(
"---test start-------");
// 模擬多3個窗口售票
TicketThread ticketThread1 = new TicketThread();
TicketThread ticketThread2
= new TicketThread();
TicketThread ticketThread3
= new TicketThread();
// 啟動線程售票
ticketThread1.start();
ticketThread2.start();
ticketThread3.start();
System.out.println(
"======等待售票============");
Thread.sleep(
5 * 1000);
System.out.println(
"---test end-------");
}

結果:



1.在售票過程中不能區分售出的票是那個窗口售出的,解決通過線程名稱判斷


2.有重複售出的票(後面的線程同步解決)


解決第一個問題,設置獲取線程名稱,通過Thread對象裏面自帶的getName,setName方法



 具體代碼


設置線程名稱



 獲取線程名稱



 上面講了繼承的方式獲取線程名稱,那麼實現接口Runnable的方式怎麼獲取設置勒


繼承Thread的方式,可以通過getName的方式獲取當前線程的名稱?
那使用Runnable的方式,能通過getName獲取嘛?


getName方法是Thread類的,但是TicketThread現在並沒有繼承Thread類,而是實現了Runnable接口.


問題:如果實現Runnable接口,怎麼獲取線程名稱?


 思考:TicketThread類裏面的代碼要執行,它肯定存在於某個線程中, 就比如寫個helloword打印語句,是不是也處於一個主線程中,那這裏怎麼獲取線程名稱?
通過動態獲取,當程序正在執行的時候,獲取當前正在執行的線程名稱。怎麼獲取?
在Thread類裏面有個靜態的方法currentThread() 方法,返回當前正在執行的線程引用;


Thread.currentThread().getName


那怎麼設置線程名稱?


Thread類裏面有個name字段,相當於Thread類把它包裝了一下:


通過源碼可以發現,構造方法裏面還有可以傳一個名字:



具體實現代碼如下



 


 總結:


繼承方式設置\獲取線程名稱通過 Thread對象裏面的 setName,getName方法;


實現接口方式設置名稱通過 new Thread('線程實例對象', "線程名稱"),獲取線程名稱通過:Thread.currentThread().getName


1.6.Thread的join方法


void join() 方法 :等待該線程終止
void join(long millis) 方法 :等待該線程終止的時間最長為millis毫秒


需求: 當主線程運行到20的時候(i =20)的時候,讓JoinThread線程加進來直到執行完成,在執行主線程.


 1 package com.wfd360.thread;
2
3 import org.junit.Test;
4
5 /**
6 * @author 姿勢帝-博客園
7 * @address https://www.cnblogs.com/newAndHui/
8 * @WeChat 851298348
9 * @create 05/04 6:31
10 * @description
11 */
12 public class Test05Join {
13 /**
14 * 需求:
15 * 當主線程for循環到i=20時,等JoinThread線程執行完成后,在執行for循環的線程
16 * @throws InterruptedException
17 */
18 @Test
19 public void testJoinThread() throws InterruptedException {
20 System.out.println("---test start-------");
21 // 開啟線程
22 JoinThread thread = new JoinThread();
23 thread.start();
24 // 循環打印線程
25 for (int i = 0; i < 100; i++) {
26 System.out.println("======testJoinThread=========="+i);
27 Thread.sleep(1);
28 if (i==20){
29 // 等線程JoinThread執行完成
30 thread.join();
31 }
32 }
33 System.out.println("=============等待線程執行完成===================");
34 Thread.sleep(10*1000);
35 System.out.println("---test end-------");
36 }
37
38 class JoinThread extends Thread {
39 @Override
40 public void run() {
41 for (int i = 0; i < 100; i++) {
42 System.out.println("=====JoinThread=======" + i);
43 // 模擬處理很多業務耗時1毫秒
44 try {
45 Thread.sleep(1);
46 } catch (InterruptedException e) {
47 e.printStackTrace();
48 }
49 }
50 }
51 }
52 }

Test05Join

1.7.線程優先級


直接上代碼


 1 package com.wfd360.thread;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/05 8:17
8 * @description <p>
9 * 1.==>線程優先級的理解:
10 * 線程的優先級和生活中類似,高優先級線程的執行優先於低優先級線程;
11 * 並不是絕對的,可能優先級高的線程優先 比 優先級低的線程先執行,只能說,高優先級的線程優先執行的幾率更多;
12 * (比如兩個線程,一個優先級高,一個優先級低,如果一共運行一個小時,優先級高的線程執行遠遠大於優先級低的但是並不是說優先級高的先執行完,
13 * 在執行優先級低的)
14 * 2.==>重新設置線程優先級
15 * int getPriority() 返回線程的優先級。
16 * void setPriority(int newPriority) 更改線程的優先級。Java線程的優先級從1到10級別,值越大優先級越高.
17 * 3.==>線程的默認優先級受創建線程的環境影響,默認值5,自定義線程的默認優先級和創建它的環境的線程優先級一致
18 * </p>
19 */
20 public class Test06Priority {
21 /**
22 * 測試獲取線程優先級,設置線程優先級,驗證線程優先級受創建環境影響
23 * @param args
24 */
25 public static void main(String[] args) {
26 Thread threadMain = Thread.currentThread();
27 // 獲取默認優先級数字
28 System.out.println("main線程默認優先級:" +threadMain.getPriority());// 5
29 // 重新設置默認優先級数字
30 threadMain.setPriority(8);
31 // 再次重新獲取優先級数字
32 System.out.println("main線程修改后的優先級:" +threadMain.getPriority());// 8
33 // 創建一個線程查看優先級
34 Thread thread = new Thread();
35 System.out.println("thread線程的優先級:" +thread.getPriority());// 8 受創建環境影響
36 }
37 }

1.8.後台線程,即守護線程


直接看代碼


 1 package com.wfd360.thread;
2
3 import com.wfd360.thread.demo04Daemon.DaemonThreaad;
4
5 /**
6 * @author 姿勢帝-博客園
7 * @address https://www.cnblogs.com/newAndHui/
8 * @WeChat 851298348
9 * @create 05/05 9:12
10 * @description <p>
11 * 後台線程,即守護線程
12 * 後台線程:指為其他線程提供服務的線程,也稱為守護線程。JVM的垃圾回收線程就是一個後台線程。
13 * 需求:嘗試把線程標記為後台線程或者標記為(前台)線程;
14 * Thread類提供的方法:
15 * 方法1: void setDaemon(boolean on) 將該線程標記為守護線程或用戶線程,true為後台線程,false為用戶線程(前台線下)
16 * 怎樣測試該線程是否是守護線程?
17 * 方法2:isDaemon() 測試該線程是否為守護線程. true為後台線程,false為用戶線程(前台線下)
18 * <p>
19 * 結論1:活動的線程(已經在執行的線程t.start())不能設置後台線程,即主線程不能設置為後台線程。
20 * 結論2: 自定義線程的默認狀態和環境有關,後台線程中創建的線程默認是後台線程,前台線程中創建的線程為前台線程.
21 * 結論3: 前台線程執行完后,會直接關閉後台線程,即自定義的後台線程不一定能執行完成
22 * </p>
23 */
24 public class Test07Daemon {
25 /**
26 * 測試1
27 * 查看主線程的狀態,嘗試更改
28 * 結論:活動的線程不能設置為後台線程
29 *
30 * @param args
31 */
32 public static void main1(String[] args) {
33 Thread threadMain = Thread.currentThread();
34 System.out.println("是後台線程么:" + threadMain.isDaemon());// false
35 threadMain.setDaemon(true); // 報錯,活動的線程不能設置為後台線程
36 System.out.println("修改后是後台線程么:" + threadMain.isDaemon());
37 }
38
39 /**
40 * 測試2
41 * 查看主線程中 創建線程的狀態,嘗試更改;
42 *
43 * @param args
44 */
45 public static void main2(String[] args) {
46 Thread thread = new Thread();
47 // false
48 System.out.println("是後台線程么:" + thread.isDaemon());
49 // 修改為後台線程
50 thread.setDaemon(true);
51 System.out.println("修改后是後台線程么:" + thread.isDaemon());
52 }
53
54 /**
55 * 測試3
56 * 查看主線程中 創建線程的狀態,嘗試更改,讓線程處於活動狀態在修改->報錯;
57 *
58 * @param args
59 */
60 public static void main3(String[] args) {
61 DaemonThread thread = new DaemonThread();
62 // 讓線程處於活躍狀態
63 thread.start();
64 // false
65 System.out.println("是後台線程么:" + thread.isDaemon());
66 // 修改為後台線程,報錯,當前已經是活躍狀態(thread.start())不能修改為後台線程
67 thread.setDaemon(true);
68 System.out.println("修改后是後台線程么:" + thread.isDaemon());
69 }
70
71 /**
72 * 測試4
73 * 前台線程執行完后,會直接關閉後台線程,即如果後台線程不一定能執行完成
74 * 可以通過修改等待執行時間來觀察DaemonThread線程的數組輸出變化
75 *
76 * @param args
77 */
78 public static void main(String[] args) throws InterruptedException {
79 DaemonThread thread = new DaemonThread();
80 // 修改為後台線程
81 thread.setDaemon(true);
82 // 讓線程處於活躍狀態
83 thread.start();
84 System.out.println("========等待後台線程執行============");
85 Thread.sleep(5 * 1000);
86 }
87 }

 1 package com.wfd360.thread.demo04Daemon;
2
3 /**
4 * @author 姿勢帝-博客園
5 * @address https://www.cnblogs.com/newAndHui/
6 * @WeChat 851298348
7 * @create 05/05 9:27
8 * @description
9 */
10 public class DaemonThread extends Thread {
11 @Override
12 public void run() {
13 for (int i = 0; i < 10; i++) {
14 System.out.println("===="+i);
15 try {
16 Thread.sleep(1000);
17 } catch (InterruptedException e) {
18 e.printStackTrace();
19 }
20 }
21 }
22 }

DaemonThread

線程基礎相關的方法定義就先到這裏,下一篇我們將進入線程同步.


https://www.cnblogs.com/newAndHui/p/12831089.html


系統化的在線學習:點擊進入學習

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

【其他文章推薦】



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



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



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



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



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



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



Orignal From: 系統化學習多線程(一)

留言

這個網誌中的熱門文章

架構設計 | 異步處理流程,多種實現模式詳解

本文源碼:GitHub·點這裏 || GitEE·點這裏 一、異步處理 1、異步概念 異步處理不用阻塞當前線程來等待處理完成,而是允許後續操作,直至其它線程將處理完成,並回調通知此線程。 必須強調一個基礎邏輯,異步是一種設計理念,異步操作不等於多線程,MQ中間件,或者消息廣播,這些是可以實現異步處理的方式。 同步處理和異步處理相對,需要實時處理並響應,一旦超過時間會結束會話,在該過程中調用方一直在等待響應方處理完成並返回。同步類似電話溝通,需要實時對話,異步則類似短信交流,發送消息之後無需保持等待狀態。 2、異步處理優點 雖然異步處理不能實時響應,但是處理複雜業務場景,多數情況都會使用異步處理。 異步可以解耦業務間的流程關聯,降低耦合度; 降低接口響應時間,例如用戶註冊,異步生成相關信息表; 異步可以提高系統性能,提升吞吐量; 流量削峰即把請求先承接下來,然後在異步處理; 異步用在不同服務間,可以隔離服務,避免雪崩; 異步處理的實現方式有很多種,常見多線程,消息中間件,發布訂閱的廣播模式,其根據邏輯在於先把請求承接下來,放入容器中,在從容器中把請求取出,統一調度處理。 注意 :一定要監控任務是否產生積壓過度情況,任務如果積壓到雪崩之勢的地步,你會感覺每一片雪花都想勇闖天涯。 3、異步處理模式 異步流程處理的實現有好多方式,但是實際開發中常用的就那麼幾種,例如: 基於接口異步響應,常用在第三方對接流程; 基於消息生產和消費模式,解耦複雜流程; 基於發布和訂閱的廣播模式,常見系統通知 異步適用的業務場景,對數據強一致性的要求不高,異步處理的數據更多時候追求的是最終一致性。 二、接口響應異步 1、流程描述 基於接口異步響應的方式,有一個本地業務服務,第三方接口服務,流程如下: 本地服務發起請求,調用第三方服務接口; 請求包含業務參數,和成功或失敗的回調地址; 第三方服務實時響應流水號,作為該調用的標識; 之後第三方服務處理請求,得到最終處理結果; 如果處理成功,回調本地服務的成功通知接口; 如果處理失敗,回調本地服務的失敗通知接口; 整個流程基於部分異步和部分實時的模式,完整處理; 注意 :如...

.NET Core前後端分離快速開發框架(Core.3.0+AntdVue)

.NET Core前後端分離快速開發框架(Core.3.0+AntdVue) 目錄 引言 時間真快,轉眼今年又要過去了。回想今年,依次開源發布了 Colder.Fx.Net.AdminLTE(254Star) 、 Colder.Fx.Core.AdminLTE(335Star) 、 DotNettySocket(82Star) 、 IdHelper(47Star) ,這些框架及組件都是本着以實際出發,實事求是的態度,力求提高開發效率(我自己都是第一個使用者),目前來看反響不錯。但是隨着前端和後端技術的不斷變革,尤其是前端,目前大環境已經是前後端完全分離為主的開發模式,在這樣的大環境和必然趨勢之下,傳統的MVC就顯得有些落伍了。在這樣的背景下,一款前後端分離的.NET開發框架就顯得尤為必要,由此便定了框架的升級目標: 前後端分離 。 首先後端技術的選擇,從目前的數據來看,.NET Core的發展遠遠快於.NET Framework,最簡單的分析就是Colder.Fx.Core.AdminLTE發布比Colder.Fx.Net.AdminLTE晚,但是星星卻後來居上而且比前者多30%,並且這個差距在不斷擴大,由點及面的分析可以看出我們廣大.NET開發人員學習的熱情和积極向上的態度,並不是某些人所認為的那麼不堪( 走自己的路,讓別人說去吧 )。大環境上微軟积極擁抱開源,大力發展.NET Core, 可以說前途一片光明。因此後端決定採用 .NET Core3.0 ,不再浪費精力去支持.NET Framework。 然後是前端技術選擇,首選是三大js框架選擇,也是從實際出發,Vue相對其它而言更加容易上手,並且功能也毫不遜色,深得各種大小公司喜歡,如果偏要說缺點的話,那就是對TS支持不行,但是即將發布Vue3.0肯定會改變這一缺陷。選擇了Vue之後,然後就是UI框架的選擇了,這裏的選擇更多了,我選擇了Ant Design Vue,理由便是簡潔方便,十分符合我的設計理念。 技術選型完畢之後便...

台北市住宅、社區建創儲能設備 最高可獲600萬元補助

為了推廣分散式發電,台北市環保局預計補助1億元供住宅社區設置創能、儲能設備,計有3種方案可供選擇。環保局說明,每案補助額度不超過建制總經費49%,社區每案最高可獲200萬至600萬元補助,住宅每案補助上限100萬元,5月1日起開放申請。 環保局說明,台北市溫室氣體排放量7成以上來自住商部門,其中以使用電力造成間接溫室氣體排放為大宗,台北市平均年用電量約159.86億度,1度電約等同排放0.5公斤二氧化碳,若想達成2050年淨零排放目標,僅靠節能減碳無法達成,必須發展綠色創能、儲能,並且參考歐洲、日本的做法,採分散式發電方式,推廣到社區、住家、商辦,達到供電自給自足目標。 因此,環保局推出「台北市住宅社區創能儲能及節能補助計畫」,補助對象為台北市轄內房屋所有權人及社區管理委員會,補助方案共計3種,每一申請人或每一場址僅能獲1次補助,每案補助額度不超過建置總經費49%為限,5月1日到7月31日開放申請,但補助經費用完即停止申請。 環保局說明,方案A補助對象以社區為主,公共區域申請創能儲能及節能項目,每案補助上限新台幣600萬元;方案B分為住宅或社區公共區域申請創能搭配儲能項目(創能或儲能方案不得單獨申請),社區每案補助上限新台幣400萬元,住宅每案補助上限100萬元。方案C補助對象也是社區,公共區域申請節能項目,每案補助上限新台幣200萬元。 網頁設計 最專業,超強功能平台可客製,窩窩以「數位行銷」「品牌經營」「網站與應用程式」「印刷品設計」等四大主軸,為每一位客戶客製建立行銷脈絡及洞燭市場先機,請問 台中電動車 哪裡在賣比較便宜可以到台中景泰電動車門市去看看總店:臺中市潭子區潭秀里雅潭路一段102-1號。 電動車補助 推薦評價好的 iphone維修 中心擁有專業的維修技術團隊,同時聘請資深iphone手機維修專家,現場說明手機問題,快速修理,沒修好不收錢住家的頂樓裝 太陽光電 聽說可發揮隔熱功效一線推薦東陽能源擁有核心技術、產品研發、系統規劃設置、專業團隊的太陽能發電廠商。 網頁設計 一頭霧水該從何著手呢? 回頭車 貨運收費標準宇安交通關係企業,自成立迄今,即秉持著「以誠待人」、「以實處事」的企業信念 台中搬家公司 教你幾個打包小技巧,輕鬆整理裝箱!還在煩惱搬家費用要多少哪?台中大展搬家線上試算搬家費用,從此不再擔心「物品怎麼計費」、「多...