Java線程小刀牛試

来源:https://www.cnblogs.com/yuxiang1/archive/2019/04/01/10639609.html
-Advertisement-
Play Games

線程簡介 什麼是線程 現代操作系統調度的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程里可以創建多個線程,這些線程都擁有各自的計數器、堆棧和局部變數等屬性,並且能夠訪問共用的記憶體變數。 線程生命周期 java.lang.Thread.State 中定義了 6  ...


線程簡介

什麼是線程

現代操作系統調度的最小單元是線程,也叫輕量級進程(Light Weight Process),在一個進程里可以創建多個線程,這些線程都擁有各自的計數器、堆棧和局部變數等屬性,並且能夠訪問共用的記憶體變數。

線程生命周期

java.lang.Thread.State 中定義了 6 種不同的線程狀態,在給定的一個時刻,線程只能處於其中的一個狀態。

以下是各狀態的說明,以及狀態間的聯繫:

  • 開始(New) - 還沒有調用 start() 方法的線程處於此狀態。
  • 可運行(Runnable) - 已經調用了 start() 方法的線程狀態。此狀態意味著,線程已經準備好了,一旦被線程調度器分配了 CPU 時間片,就可以運行線程。
  • 阻塞(Blocked) - 阻塞狀態。線程阻塞的線程狀態等待監視器鎖定。處於阻塞狀態的線程正在等待監視器鎖定,以便在調用 Object.wait() 之後輸入同步塊/方法或重新輸入同步塊/方法。
  • 等待(Waiting) - 等待狀態。一個線程處於等待狀態,是由於執行了 3 個方法中的任意方法:
    • Object.wait()
    • Thread.join()
    • LockSupport.park()
  • 定時等待(Timed waiting) - 等待指定時間的狀態。一個線程處於定時等待狀態,是由於執行了以下方法中的任意方法:終止(Terminated) - 線程 run() 方法執行結束,或者因異常退出了 run() 方法,則該線程結束生命周期。死亡的線程不可再次復生。
    • Thread.sleep(sleeptime)
    • Object.wait(timeout)
    • Thread.join(timeout)
    • LockSupport.parkNanos(timeout)
    • LockSupport.parkUntil(timeout)

啟動和終止線程

構造線程

構造線程主要有三種方式

  • 繼承 Thread 類
  • 實現 Runnable 介面
  • 實現 Callable 介面

繼承 Thread 類

通過繼承 Thread 類構造線程的步驟:

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

示例:

public class ThreadDemo02 {

    public static void main(String[] args) {
        Thread02 mt1 = new Thread02("線程A "); // 實例化對象
        Thread02 mt2 = new Thread02("線程B "); // 實例化對象
        mt1.start(); // 調用線程主體
        mt2.start(); // 調用線程主體
    }

    static class Thread02 extends Thread {

        private int ticket = 5;

        Thread02(String name) {
            super(name);
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (this.ticket > 0) {
                    System.out.println(this.getName() + " 賣票:ticket = " + ticket--);
                }
            }
        }
    }
}

 

實現 Runnable 介面

通過實現 Runnable 介面構造線程的步驟:

  • 定義 Runnable 介面的實現類,並重寫該介面的 run() 方法,該 run() 方法的方法體同樣是該線程的線程執行體。
  • 創建 Runnable 實現類的實例,並依此實例作為 Thread 的 target 來創建 Thread 對象,該 Thread 對象才是真正的線程對象。
  • 調用線程對象的 start() 方法來啟動該線程。

示例:

public class RunnableDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread("Runnable 線程"); // 實例化對象
        new Thread(t).run(); // 調用線程主體
        new Thread(t).run(); // 調用線程主體
        new Thread(t).run(); // 調用線程主體
    }

    static class MyThread implements Runnable {

        private int ticket = 5;
        private String name;

        MyThread(String name) {
            this.name = name;
        }

        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                if (this.ticket > 0) {
                    System.out.println(this.name + " 賣票:ticket = " + ticket--);
                }
            }
        }
    }
}

 

實現 Callable 介面

通過實現 Callable 介面構造線程的步驟:

  • 創建 Callable 介面的實現類,並實現 call() 方法,該 call() 方法將作為線程執行體,並且有返回值。
  • 創建 Callable 實現類的實例,使用 FutureTask 類來包裝 Callable 對象,該 FutureTask 對象封裝了該 Callable 對象的 call() 方法的返回值。
  • 使用 FutureTask 對象作為 Thread 對象的 target 創建並啟動新線程。
  • 調用 FutureTask 對象的 get() 方法來獲得子線程執行結束後的返回值。

示例:

public class CallableAndFutureDemo {

    public static void main(String[] args) {
        Callable<Integer> callable = () -> new Random().nextInt(100);
        FutureTask<Integer> future = new FutureTask<>(callable);
        new Thread(future).start();
        try {
            Thread.sleep(1000);// 可能做一些事情
            System.out.println(future.get());
        } catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
        }
    }
}

 

三種創建線程方式對比

  • 實現 Runnable 介面優於繼承 Thread 類,因為實現介面方式更便於擴展類。
  • 實現 Runnable 介面的線程沒有返回值;而實現 Callable 介面的線程有返回值。

中斷線程

當一個線程運行時,另一個線程可以直接通過 interrupt() 方法中斷其運行狀態。

public class ThreadInterruptDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        Thread t = new Thread(mt, "線程"); // 實例化Thread對象
        t.start(); // 啟動線程
        try {
            Thread.sleep(2000); // 線程休眠2秒
        } catch (InterruptedException e) {
            System.out.println("3、休眠被終止");
        }
        t.interrupt(); // 中斷線程執行
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            System.out.println("1、進入run()方法");
            try {
                Thread.sleep(10000); // 線程休眠10秒
                System.out.println("2、已經完成了休眠");
            } catch (InterruptedException e) {
                System.out.println("3、休眠被終止");
                return; // 返回調用處
            }
            System.out.println("4、run()方法正常結束");
        }
    }
}

 

終止線程

Thread 中的 stop 方法有缺陷,已廢棄。

安全地終止線程有兩種方法:

  1. 中斷狀態是線程的一個標識位,而中斷操作是一種簡便的線程間交互方式,而這種交互方式最適合用來取消或停止任務。
  2. 還可以利用一個 boolean 變數來控制是否需要停止任務並終止該線程。
public class ThreadStopDemo03 {

    public static void main(String[] args) throws Exception {
        MyTask one = new MyTask();
        Thread countThread = new Thread(one, "CountThread");
        countThread.start();
        // 睡眠1秒,main線程對CountThread進行中斷,使CountThread能夠感知中斷而結束
        TimeUnit.SECONDS.sleep(1);
        countThread.interrupt();
        MyTask two = new MyTask();
        countThread = new Thread(two, "CountThread");
        countThread.start();
        // 睡眠1秒,main線程對Runner two進行取消,使CountThread能夠感知on為false而結束
        TimeUnit.SECONDS.sleep(1);
        two.cancel();
    }

    private static class MyTask implements Runnable {

        private long i;
        private volatile boolean on = true;

        @Override
        public void run() {
            while (on && !Thread.currentThread().isInterrupted()) {
                i++;
            }
            System.out.println("Count i = " + i);
        }

        void cancel() {
            on = false;
        }
    }
}

 

Thread 中的重要方法
  • run - 線程的執行實體。
  • start - 線程的啟動方法。
  • setNamegetName - 可以通過 setName()、 getName() 來設置、獲取線程名稱。
  • setPrioritygetPriority - 在 Java 中,所有線程在運行前都會保持在就緒狀態,那麼此時,哪個線程優先順序高,哪個線程就有可能被先執行。可以通過 setPriority、getPriority 來設置、獲取線程優先順序。
  • setDaemonisDaemon - 可以使用 setDaemon() 方法設置線程為守護線程;可以使用 isDaemon() 方法判斷線程是否為守護線程。
  • isAlive - 可以通過 isAlive 來判斷線程是否啟動。
  • interrupt - 當一個線程運行時,另一個線程可以直接通過 interrupt() 方法中斷其運行狀態。
  • join - 使用 join() 方法讓一個線程強制運行,線程強制運行期間,其他線程無法運行,必須等待此線程完成之後才可以繼續執行。
  • Thread.sleep - 使用 Thread.sleep() 方法即可實現休眠。
  • Thread.yield - 可以使用 Thread.yield() 方法將一個線程的操作暫時讓給其他線程執行。

設置/獲取線程名稱

在 Thread 類中可以通過 setName()、 getName() 來設置、獲取線程名稱。

public class ThreadNameDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        new Thread(mt).start(); // 系統自動設置線程名稱
        new Thread(mt, "線程-A").start(); // 手工設置線程名稱
        Thread t = new Thread(mt); // 手工設置線程名稱
        t.setName("線程-B");
        t.start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "運行,i = " + i); // 取得當前線程的名字
            }
        }
    }
}

 

判斷線程是否啟動

在 Thread 類中可以通過 isAlive() 來判斷線程是否啟動。

public class ThreadAliveDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        Thread t = new Thread(mt, "線程"); // 實例化Thread對象
        System.out.println("線程開始執行之前 --> " + t.isAlive()); // 判斷是否啟動
        t.start(); // 啟動線程
        System.out.println("線程開始執行之後 --> " + t.isAlive()); // 判斷是否啟動
        for (int i = 0; i < 3; i++) {
            System.out.println(" main運行 --> " + i);
        }
        // 以下的輸出結果不確定
        System.out.println("代碼執行之後 --> " + t.isAlive()); // 判斷是否啟動

    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 3; i++) {
                System.out.println(Thread.currentThread().getName() + "運行,i = " + i);
            }
        }
    }
}

 

守護線程

在 Java 程式中,只要前臺有一個線程在運行,則整個 Java 進程就不會消失,所以此時可以設置一個守護線程,這樣即使 Java 進程結束了,此守護線程依然會繼續執行。可以使用 setDaemon() 方法設置線程為守護線程;可以使用 isDaemon() 方法判斷線程是否為守護線程。

public class ThreadDaemonDemo {

    public static void main(String[] args) {
        Thread t = new Thread(new MyThread(), "線程");
        t.setDaemon(true); // 此線程在後臺運行
        System.out.println("線程 t 是否是守護進程:" + t.isDaemon());
        t.start(); // 啟動線程
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            while (true) {
                System.out.println(Thread.currentThread().getName() + "在運行。");
            }
        }
    }
}

 

設置/獲取線程優先順序

在 Java 中,所有線程在運行前都會保持在就緒狀態,那麼此時,哪個線程優先順序高,哪個線程就有可能被先執行。

public class ThreadPriorityDemo {

    public static void main(String[] args) {
        System.out.println("主方法的優先順序:" + Thread.currentThread().getPriority());
        System.out.println("MAX_PRIORITY = " + Thread.MAX_PRIORITY);
        System.out.println("NORM_PRIORITY = " + Thread.NORM_PRIORITY);
        System.out.println("MIN_PRIORITY = " + Thread.MIN_PRIORITY);

        Thread t1 = new Thread(new MyThread(), "線程A"); // 實例化線程對象
        Thread t2 = new Thread(new MyThread(), "線程B"); // 實例化線程對象
        Thread t3 = new Thread(new MyThread(), "線程C"); // 實例化線程對象
        t1.setPriority(Thread.MIN_PRIORITY); // 優先順序最低
        t2.setPriority(Thread.MAX_PRIORITY); // 優先順序最低
        t3.setPriority(Thread.NORM_PRIORITY); // 優先順序最低
        t1.start(); // 啟動線程
        t2.start(); // 啟動線程
        t3.start(); // 啟動線程
    }

 



    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(500); // 線程休眠
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 取得當前線程的名字
                String out =
                    Thread.currentThread().getName() + ",優先順序:" + Thread.currentThread().getPriority() + ",運行:i = " + i;
                System.out.println(out);
            }
        }
    }
}

 

 

線程間通信

wait/notify/notifyAll

wait、notify、notifyAll 是 Object 類中的方法。

  • wait - 線程自動釋放其占有的對象鎖,並等待 notify。
  • notify - 喚醒一個正在 wait 當前對象鎖的線程,並讓它拿到對象鎖。
  • notifyAll - 喚醒所有正在 wait 前對象鎖的線程。

生產者、消費者示例:

public class ThreadWaitNotifyDemo02 {

    private static final int QUEUE_SIZE = 10;
    private static final PriorityQueue<Integer> queue = new PriorityQueue<>(QUEUE_SIZE);

    public static void main(String[] args) {
        new Producer("生產者A").start();
        new Producer("生產者B").start();
        new Consumer("消費者A").start();
        new Consumer("消費者B").start();
    }

    static class Consumer extends Thread {

        Consumer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == 0) {
                        try {
                            System.out.println("隊列空,等待數據");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.poll(); // 每次移走隊首元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 從隊列取走一個元素,隊列當前有:" + queue.size() + "個元素");
                }
            }
        }
    }

    static class Producer extends Thread {

        Producer(String name) {
            super(name);
        }

        @Override
        public void run() {
            while (true) {
                synchronized (queue) {
                    while (queue.size() == QUEUE_SIZE) {
                        try {
                            System.out.println("隊列滿,等待有空餘空間");
                            queue.wait();
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                            queue.notifyAll();
                        }
                    }
                    queue.offer(1); // 每次插入一個元素
                    queue.notifyAll();
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName() + " 向隊列取中插入一個元素,隊列當前有:" + queue.size() + "個元素");
                }
            }
        }
    }
}

 

線程的禮讓

線上程操作中,可以使用 Thread.yield() 方法將一個線程的操作暫時讓給其他線程執行。

public class ThreadYieldDemo {

    public static void main(String[] args) {
        MyThread t = new MyThread();
        new Thread(t, "線程A").start();
        new Thread(t, "線程B").start();
    }

    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 5; i++) {
                try {
                    Thread.sleep(1000);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName() + "運行,i = " + i);
                if (i == 2) {
                    System.out.print("線程禮讓:");
                    Thread.yield();
                }
            }
        }
    }
}

 

線程的強制執行

線上程操作中,可以使用 join() 方法讓一個線程強制運行,線程強制運行期間,其他線程無法運行,必須等待此線程完成之後才可以繼續執行。

public class ThreadJoinDemo {

    public static void main(String[] args) {
        MyThread mt = new MyThread(); // 實例化Runnable子類對象
        Thread t = new Thread(mt, "mythread"); // 實例化Thread對象
        t.start(); // 啟動線程
        for (int i = 0; i < 50; i++) {
            if (i > 10) {
                try {
                    t.join(); // 線程強制運行
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
            System.out.println("Main 線程運行 --> " + i);
        }
    }

 



    static class MyThread implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 50; i++) {
                System.out.println(Thread.currentThread().getName() + " 運行,i = " + i); // 取得當前線程的名字
            }
        }
    }
}

 

 

線程的休眠

直接使用 Thread.sleep() 方法即可實現休眠。

public class ThreadSleepDemo {

    public static void main(String[] args) {
        new Thread(new MyThread("線程A", 1000)).start();
        new Thread(new MyThread("線程A", 2000)).start();
        new Thread(new MyThread("線程A", 3000)).start();
    }

    static class MyThread implements Runnable {

        private String name;
        private int time;

        private MyThread(String name, int time) {
            this.name = name; // 設置線程名稱
            this.time = time; // 設置休眠時間
        }

        @Override
        public void run() {
            try {
                Thread.sleep(this.time); // 休眠指定的時間
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(this.name + "線程,休眠" + this.time + "毫秒。");
        }
    }
}

 

ThreadLocal

ThreadLocal,很多地方叫做線程本地變數,也有些地方叫做線程本地存儲,其實意思差不多。可能很多朋友都知道 ThreadLocal 為變數在每個線程中都創建了一個副本,那麼每個線程可以訪問自己內部的副本變數。

源碼

ThreadLocal 的主要方法:

public class ThreadLocal<T> {
    public T get() {}
    public void remove() {}
    public void set(T value) {}
    public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {}
}

 

  • get()方法是用來獲取 ThreadLocal 在當前線程中保存的變數副本。
  • set()用來設置當前線程中變數的副本。
  • remove()用來移除當前線程中變數的副本。
  • initialValue()是一個 protected 方法,一般是用來在使用時進行重寫的,它是一個延遲載入方法,下麵會詳細說明。
get() 源碼實現
get 源碼

public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        ThreadLocalMap.Entry e = map.getEntry(this);
        if (e != null) {
            @SuppressWarnings("unchecked")
            T result = (T)e.value;
            return result;
        }
    }
    return setInitialValue();
}

 

  1. 取得當前線程。
  2. 通過 getMap() 方法獲取 ThreadLocalMap。
  3. 成功,返回 value;失敗,返回 setInitialValue()。
ThreadLocalMap 源碼實現

ThreadLocalMap 源碼

ThreadLocalMap 是 ThreadLocal 的一個內部類。

ThreadLocalMap 的 Entry 繼承了 WeakReference,並且使用 ThreadLocal 作為鍵值。

setInitialValue 源碼實現
private T setInitialValue() {
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}

 

如果 map 不為空,就設置鍵值對;為空,再創建 Map,看一下 createMap 的實現:

void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

 

ThreadLocal 源碼小結

至此,可能大部分朋友已經明白了 ThreadLocal 是如何為每個線程創建變數的副本的:

  1. 在每個線程 Thread 內部有一個 ThreadLocal.ThreadLocalMap 類型的成員變數 threadLocals,這個 threadLocals 就是用來存儲實際的變數副本的,鍵值為當前 ThreadLocal 變數,value 為變數副本(即 T 類型的變數)。
  2. 在 Thread 裡面,threadLocals 為空,當通過 ThreadLocal 變數調用 get()方法或者 set()方法,就會對 Thread 類中的 threadLocals 進行初始化,並且以當前 ThreadLocal 變數為鍵值,以 ThreadLocal 要保存的副本變數為 value,存到 threadLocals。
  3. 在當前線程裡面,如果要使用副本變數,就可以通過 get 方法在 threadLocals 裡面查找。

示例

ThreadLocal 最常見的應用場景為用於解決資料庫連接、Session 管理等問題。

示例 - 資料庫連接

private static ThreadLocal<Connection> connectionHolder
= new ThreadLocal<Connection>() {
public Connection initialValue() {
    return DriverManager.getConnection(DB_URL);
}
};

public static Connection getConnection() {
return connectionHolder.get();
}

 

示例 - Session 管理

private static final ThreadLocal threadSession = new ThreadLocal();

public static Session getSession() throws InfrastructureException {
    Session s = (Session) threadSession.get();
    try {
        if (s == null) {
            s = getSessionFactory().openSession();
            threadSession.set(s);
        }
    } catch (HibernateException ex) {
        throw new InfrastructureException(ex);
    }
    return s;
}

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 官網:www.fhadmin.org 工作流模塊 1.模型管理 :web線上流程設計器、預覽流程xml、導出xml、部署流程 2.流程管理 :導入導出流程資源文件、查看流程圖、根據流程實例反射出流程模型、激活掛起 3.運行中流程:查看流程信息、當前任務節點、當前流程圖、作廢暫停流程、指派待辦人 4. ...
  • 記錄一下最近整理的spring boot項目 項目地址:https://gitee.com/xl0917/spring-boot 1.選擇Spring Initializr 一直點擊next,直到創建完成 2.創建spring boot子模塊,創建無任何模板的maven項目 3.項目結構 4.pom ...
  • 前言 去年年底,博主有購房的意願,本來是打算在青島市北購房,怎奈工作變動,意向轉移到了李滄,坐等了半年以後,最終選擇在紅島附近購置了期房。 也許一些知道青島紅島的小伙伴會問我,為什麼會跑到那鳥不拉屎的地方去買房子,目前只能是一個字:"賭、賭、賭",重要的事情說三遍。下麵來分析一下,我為什麼沒有在李滄 ...
  • 驗證碼探究 如果你是一個數據挖掘愛好者,那麼驗證碼是你避免不過去的一個天坑,和各種驗證碼鬥爭,必然是你成長的一條道路,接下來的幾篇文章,我會儘量的找到各種驗證碼,並且去嘗試解決掉它,中間有些技術甚至我都沒有見過,來吧,一起Coding吧 數字+字母的驗證碼 我隨便在百度圖片搜索了一個驗證碼,如下 今 ...
  • 1.別瞎寫,方法里能用封裝好的類,就別自己寫HashMap. 2.方法名,整的方法名都是啥?退出close,用out. 3.git提交版本,自己寫的代碼,註釋,提交版本的時候,一定要清理掉.每個判斷能不能用單元測試測測. 4.啥時候捕獲錯誤,什麼時候拋出錯誤,什麼時候聲明錯誤,在方法里每個都try_ ...
  • 流,確定是筆者內心很嚮往的天堂,有他之後JAVA在處理數據就變更加的靈動。加上lambda表達不喜歡都不行。JAVA8也為流在提供另一個功能——並行流。即是有並行流,那麼是不是也有順序流。沒有錯。我前面操作的一般都是順序流。在JAVA8裡面並行流和順序流是可以轉變的。來看一個例子——筆者列印數字。 ...
  • #!/usr/bin/env python# -*- coding:utf-8 -*-# 1.簡述解釋型語言和編譯型語言的區別?"""1.解釋型語言:Python,PHP,Ruby.特點是一行一行的解釋,一行一行的傳輸給電腦,報錯行前面可以執行.2.編譯型語言:C,C++,Java,C#,Go.特 ...
  • slice 是 Go 語言一個很重要的數據結構。網上已經有很多文章寫過了,似乎沒必要再寫。但是每個人看問題的視角不同,寫出來的東西自然也不一樣。我這篇會從更底層的彙編語言去解讀它,這是一個新的世界。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...