主頁 > 後端開發 > Java 執行緒創建與常用方法

Java 執行緒創建與常用方法

2022-06-05 09:38:11 後端開發

行程與執行緒

行程

  • 程式由指令和資料組成,但這些指令要運行,資料要讀寫,就必須將指令加載至 CPU,資料加載至記憶體,在指令運行程序中還需要用到磁盤、網路等設備,行程就是用來加載指令、管理記憶體、管理 IO 的
  • 當一個程式被運行,從磁盤加載這個程式的代碼至記憶體,這時就開啟了一個行程

執行緒

  • 一個行程之內可以分為一到多個執行緒,
  • 一個執行緒就是一個指令流,將指令流中的一條條指令以一定的順序交給 CPU 執行
  • Java 中,執行緒作為最小調度單位,行程作為資源分配的最小單位, 在 windows 中行程是不活動的,只是作為執行緒的容器

行程與執行緒的區別

  • 行程基本上相互獨立的,而執行緒存在于行程內,是行程的一個子集
  • 行程擁有共享的資源,如記憶體空間等,供其內部的執行緒共享
  • 行程間通信較為復雜
    • 同一臺計算機的行程通信稱為 IPC(Inter-process communication)
    • 不同計算機之間的行程通信,需要通過網路,并遵守共同的協議,例如 HTTP
  • 執行緒通信相對簡單,因為它們共享行程內的記憶體,一個例子是多個執行緒可以訪問同一個共享變數
  • 執行緒更輕量,執行緒背景關系切換成本一般上要比行程背景關系切換低

并行與并發

單核 cpu 下,執行緒實際還是 串行執行 的,作業系統中有一個組件叫做任務調度器,將 cpu 的時間片(windows下時間片最小約為 15 毫秒)分給不同的程式使用,只是由于 cpu 在執行緒間(時間片很短)的切換非常快,人類感覺是 同時運行的 ,總結為一句話就是: 微觀串行,宏觀并行 ,一般會將這種 執行緒輪流使用 CPU 的做法稱為并發 (concurrent)

多核 cpu下,每個 核(core) 都可以調度運行執行緒,這時候執行緒可以是并行的,

Java 執行緒

創建和運行執行緒

  • 直接使用 Thread

    package create;
    
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j(topic = "c.ThreadCre")
    public class ThreadCre {
        public static void main(String[] args) {
    
            Thread t = new Thread(){
                @Override
                public void run() {
                    log.debug("running");
                }
            };
    
            t.start();
    
            log.debug("running");
    
        }
    }
    
  • 使用 Runnable 配合 Thread

    package create;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j(topic = "c.RunnableCre")
    public class RunnableCre {
        public static void main(String[] args) {
            Runnable r = new Runnable() {
                @Override
                public void run() {
                    log.debug("running");
                }
            };
    
            Thread t = new Thread(r,"t2");
    
            t.start();
        }
    }
    

    使用 lambda 方式簡化

    package create;
    
    import lombok.extern.slf4j.Slf4j;
    
    @Slf4j(topic = "c.RunnableCre")
    public class RunnableCre {
        public static void main(String[] args) {
            Runnable r = () -> { log.debug("running"); };
    
            Thread t = new Thread(r,"t2");
    
            t.start();
        }
    }
    
  • FutureTask 配合 Thread

    package create;
    
    import lombok.extern.slf4j.Slf4j;
    
    import java.util.concurrent.Callable;
    import java.util.concurrent.ExecutionException;
    import java.util.concurrent.FutureTask;
    
    @Slf4j(topic = "c.FutureTaskCre")
    public class FutureTaskCre {
        public static void main(String[] args) throws ExecutionException, InterruptedException {
            FutureTask<Integer> task = new FutureTask<>(new Callable<Integer>() {
                @Override
                public Integer call() throws Exception {
                    log.debug("running...");
                    Thread.sleep(1000);
                    return 100;
                }
            });
    
            Thread t = new Thread(task,"t1");
            t.start();
    
            log.debug("{}",task.get());
        }
    }
    

Thread 與 Runnable 的關系

  • 用 Runnable 更容易與執行緒池等高級 API 配合
  • 用 Runnable 讓任務類脫離了 Thread 繼承體系,更靈活

執行緒運行的原理

堆疊與堆疊幀

每個執行緒啟動后,虛擬機就會為其分配一塊堆疊記憶體,

  • 每個堆疊由多個堆疊幀(Frame)組成,對應著每次方法呼叫時所占用的記憶體
  • 每個執行緒只能有一個活動堆疊幀,對應著當前正在執行的那個方法

執行緒背景關系切換

因為以下一些原因導致 cpu 不再執行當前的執行緒,轉而執行另一個執行緒的代碼

  • 執行緒的 cpu 時間片用完
  • 垃圾回收
  • 有更高優先級的執行緒需要運行
  • 執行緒自己呼叫了 sleep、yield、wait、join、park、synchronized、lock 等方法

當 Context Switch 發生時,需要由作業系統保存當前執行緒的狀態,并恢復另一個執行緒的狀態,Java 中對應的概念就是程式計數器(Program Counter Register),它的作用是記住下一條 jvm 指令的執行地址,是執行緒私有的

  • 狀態包括程式計數器、虛擬機堆疊中每個堆疊幀的資訊,如區域變數、運算元堆疊、回傳地址等
  • Context Switch 頻繁發生會影響性能

常見方法

方法名 static 功能說明 注意
start() 啟動一個新執行緒,在新的執行緒運行 run 方法中的代碼 start 方法只是讓執行緒進入就緒,里面的代碼不一定立刻運行(CPU的時間片還沒有分給它),每個執行緒物件的 start 方法只能呼叫一次,否則會出現例外
run() 新執行緒啟動后會呼叫的方法 如果在構造 Thread 物件時傳遞了 Runnable 引數,則執行緒啟動后會呼叫 Runnable 中的 run 方法,但可以創建 Thread 的子類物件來覆寫默認行為
join() 等待執行緒運行結束
join(long n) 等待執行緒運行結果,最多等待 n 毫秒
getId() 獲取執行緒長整型的 id
getName() 獲取執行緒名
setName(String) 修改執行緒名
getPriority() 獲取執行緒優先級
setPriority(int) 修改執行緒優先級 java中規定執行緒優先級是1~10 的整數,較大的優先級能提高該執行緒被 CPU 調度的機率
getState() 獲取執行緒狀態 Java 中執行緒狀態是用 6 個 enum 表示,分別為:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING, TERMINATED
isInterrupted() 判斷是否被打斷 不會清除 打斷標記
isAlive() 執行緒是否存活(還沒有運行完畢)
interrupt() 打斷執行緒 如果被打斷執行緒正在 sleep,wait,join 會導致被打斷的執行緒拋出 InterruptedException,并清除 打斷標記 ;如果打斷的正在運行的執行緒,則會設定 打斷標記 ;park 的執行緒被打斷,也會設定 打斷標記
interrupted() static 判斷當前執行緒是否被打斷 會清除 打斷標記
currentThread() static 獲取當前正在執行的執行緒
sleep(long n) static 讓當前執行的執行緒休眠 n 毫秒,休眠時讓出 CPU 的時間片給其他程式
yield() static 提示執行緒調度器讓出當前執行緒對CPU的使用 主要是為了測驗和除錯

start 與 run

呼叫 run

public static void main(String[] args) {
 	Thread t1 = new Thread("t1") {
 		@Override
 		public void run() {
 			log.debug(Thread.currentThread().getName());
 			FileReader.read(Constants.MP4_FULL_PATH);
 		}
 	};
    
 	t1.run();
 	log.debug("do other things ...");
}

輸出

19:39:14 [main] c.TestStart - main
19:39:14 [main] c.FileReader - read [1.mp4] start ...
19:39:18 [main] c.FileReader - read [1.mp4] end ... cost: 4227 ms
19:39:18 [main] c.TestStart - do other things ...

程式仍在 main 執行緒運行, FileReader.read() 方法呼叫還是同步的

總結

  • 直接呼叫 run 是在主執行緒中執行了 run,沒有啟動新的執行緒
  • 使用 start 是啟動新的執行緒,通過新的執行緒間接執行 run 中的代碼

sleep 與 yield

sleep

  • 呼叫 sleep 會讓當前執行緒從 Running 進入 Timed Waiting 狀態(阻塞)
  • 其它執行緒可以使用 interrupt 方法打斷正在睡眠的執行緒,這時 sleep 方法會拋出 InterruptedException
  • 睡眠結束后的執行緒未必會立刻得到執行(搶占時間片)
  • 建議用 TimeUnit 的 sleep 代替 Thread 的 sleep 來獲得更好的可讀性

yield

  • 呼叫 yield 會讓當前執行緒從 Running 進入 Runnable 就緒狀態,然后調度執行其它執行緒
  • 具體的實作依賴于作業系統的任務調度器

執行緒優先級

  • 執行緒優先級會提示(hint)調度器優先調度該執行緒,但它僅僅是一個提示,調度器可以忽略它
  • 如果 cpu 比較忙,那么優先級高的執行緒會獲得更多的時間片,但 cpu 閑時,優先級幾乎沒作用

join

等待一個執行緒執行結束

等待多個執行緒的結果

情況一:

package testJoin;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.demo1")
public class demo1 {

    static int r = 0 , r1 = 0 , r2 = 0;

    public static void main(String[] args) throws InterruptedException {
        test2();
    }

    private static void test2() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                r1 = 10;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(2000);
                r1 = 20;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long start = System.currentTimeMillis();
        t1.start();
        t2.start();
        log.debug("join begin");
        t1.join();
        log.debug("t1 join end");
        t2.join();
        log.debug("t2 join end");
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}",r1,r2,end-start);
    }
}

輸出:

14:18:02 [main] c.demo1 - join begin
14:18:03 [main] c.demo1 - t1 join end
14:18:04 [main] c.demo1 - t2 join end
14:18:04 [main] c.demo1 - r1: 20 r2: 0 cost: 2008

情況二:

package testJoin;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.demo1")
public class demo1 {

    static int r = 0 , r1 = 0 , r2 = 0;

    public static void main(String[] args) throws InterruptedException {
        test2();
    }

    private static void test2() throws InterruptedException {
        Thread t1 = new Thread(() -> {
            try {
                Thread.sleep(1000);
                r1 = 10;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        Thread t2 = new Thread(() -> {
            try {
                Thread.sleep(2000);
                r1 = 20;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        long start = System.currentTimeMillis();
        t1.start();
        t2.start();
        log.debug("join begin");
        t2.join();
        log.debug("t2 join end");
        t1.join();
        log.debug("t1 join end");
        long end = System.currentTimeMillis();
        log.debug("r1: {} r2: {} cost: {}",r1,r2,end-start);
    }
}

輸出:

14:19:19 [main] c.demo1 - join begin
14:19:21 [main] c.demo1 - t2 join end
14:19:21 [main] c.demo1 - t1 join end
14:19:21 [main] c.demo1 - r1: 20 r2: 0 cost: 2006

另外 join 也可以帶引數,是有時效的等待,當到設定時間執行緒還未給出結果,直接向下運行,不再等待,如果設定時間還沒到但是執行緒已經執行完畢,則直接向下執行,不再等待,

interrupt

打斷 sleep,wait,join 的執行緒

這幾個方法都會讓執行緒進入阻塞狀態

打斷 sleep 的執行緒, 會清空打斷狀態,以 sleep 為例

package testInterrupt;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.demo1")
public class demo1 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("sleep...");
            try {
                Thread.sleep(5000);
                //注意:sleep,wait,join等被打斷并以例外形式表現出來后
                // 會把打斷標記重新置為 false(未打斷狀態)
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        },"t1");

        t1.start();
        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
        log.debug("打斷標記:{}",t1.isInterrupted());
    }
}

輸出:

15:08:12 [t1] c.demo1 - sleep...
15:08:13 [main] c.demo1 - interrupt
15:08:13 [main] c.demo1 - 打斷標記:false
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at testInterrupt.demo1.lambda$main$0(demo1.java:11)
	at java.lang.Thread.run(Thread.java:748)

Process finished with exit code 0

打斷正常運行的執行緒打斷標記置為:true

package testInterrupt;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.demo2")
public class demo2 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            while (true){
                boolean interrupted = Thread.currentThread().isInterrupted();
                if(interrupted){
                    log.debug("被打斷了,退出回圈");
                    break;
                }
            }
        },"t1");
        t1.start();

        Thread.sleep(1000);
        log.debug("interrupt");
        t1.interrupt();
    }
}

輸出:

15:17:40 [main] c.demo2 - interrupt
15:17:40 [t1] c.demo2 - 被打斷了,退出回圈

打斷 park 執行緒

package testInterrupt;

import lombok.extern.slf4j.Slf4j;

import java.util.concurrent.locks.LockSupport;

@Slf4j(topic = "c.demo4")
public class demo4 {
    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            log.debug("park...");
            LockSupport.park();
            log.debug("unpark...");
            log.debug("打斷狀態:{}",Thread.currentThread().isInterrupted());
        },"t1");

        t1.start();

        Thread.sleep(1000);
        t1.interrupt();
    }
}

輸出:

14:16:21 [t1] c.demo4 - park...
14:16:22 [t1] c.demo4 - unpark...
14:16:22 [t1] c.demo4 - 打斷狀態:true

兩階段終止模式

package testInterrupt;

import lombok.extern.slf4j.Slf4j;

@Slf4j(topic = "c.demo3")
public class demo3 {
    public static void main(String[] args) throws InterruptedException {
        TwoPhaseTermination tpt = new TwoPhaseTermination();
        tpt.start();
        Thread.sleep(3500);
        tpt.stop();
    }
}

@Slf4j(topic = "c.TwoPhaseTermination")
class TwoPhaseTermination{
    private Thread monitor;

    //啟動監控執行緒
    public void start(){
        monitor = new Thread(() -> {
            while (true){
                Thread current = Thread.currentThread();
                if(current.isInterrupted()){
                    log.debug("料理后事");
                    break;
                }
                try {
                    Thread.sleep(1000);//情況1
                    log.debug("執行監控記錄");//情況2
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    //重新設定打斷標記
                    current.interrupt();
                }
            }
        });

        monitor.start();
    }

    //終止監控執行緒
    public void stop(){

        monitor.interrupt();
    }
}

輸出:

15:33:02 [Thread-0] c.TwoPhaseTermination - 執行監控記錄
15:33:03 [Thread-0] c.TwoPhaseTermination - 執行監控記錄
15:33:04 [Thread-0] c.TwoPhaseTermination - 執行監控記錄
java.lang.InterruptedException: sleep interrupted
	at java.lang.Thread.sleep(Native Method)
	at testInterrupt.TwoPhaseTermination.lambda$start$0(demo3.java:29)
	at java.lang.Thread.run(Thread.java:748)
15:33:04 [Thread-0] c.TwoPhaseTermination - 料理后事

Process finished with exit code 0

不推薦的方法

還有一些不推薦使用的方法,這些方法已過時,容易破壞同步代碼塊,造成執行緒死鎖

方法名 static 功能說明
stop() 停止執行緒運行
suspend() 掛起(暫停)執行緒運行
resume() 恢復執行緒運行

轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/485585.html

標籤:Java

上一篇:面向物件設計與構造2022第三單元總結

下一篇:WSL2Nginx PHPFPM在連接到上游時失敗(111:連接被拒絕),客戶端:172.23.0.1,上游:“fastcgi://172.23.0.3:9001”

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more