主頁 > 後端開發 > Java并發編程-執行緒基礎

Java并發編程-執行緒基礎

2020-10-10 21:57:17 後端開發

1. 執行緒的創建

首先我們來復習我們學習 java 時接觸的執行緒創建,這也是面試的時候喜歡問的,有人說兩種也有人說三種四種等等,其實我們不能去死記硬背,而應該深入理解其中的原理,當我們理解后就會發現所謂的創建執行緒實質都是一樣的,在我們面試的程序中如果我們能從本質出發回答這樣的問題,那么相信一定是個加分項!好了我們不多說了,開始今天的 code 之路

1.1 **繼承 Thread 類創建執行緒 **

**

  • 這是我們最常見的創建執行緒的方式,通過繼承 Thread 類來重寫 run 方法,


代碼如下:


/**
 * 執行緒類
 * url: www.i-code.online
 * @author: anonyStar
 * @time: 2020/9/24 18:55
 */
public class ThreadDemo extends Thread {
    @Override
    public void run() {
        //執行緒執行內容
        while (true){
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("ThredDemo 執行緒正在執行,執行緒名:"+ Thread.currentThread().getName());
        }
    }
}

測驗方法:

    @Test
    public void thread01(){
        Thread thread = new ThreadDemo();
        thread.setName("執行緒-1 ");
        thread.start();

        while (true){
            System.out.println("這是main主執行緒:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    }

結果:

繼承 Thread 的執行緒創建簡單,啟動時直接呼叫 start 方法,而不是直接呼叫 run 方法,直接呼叫 run 等于呼叫普通方法,并不是啟動執行緒

1.2 **實作 Runnable 介面創建執行緒 **

**

  • 上述方式我們是通過繼承來實作的,那么在 java 中提供了 Runnable 介面,我們可以直接實作該介面,實作其中的 run 方法,這種方式可擴展性更高


代碼如下:


/**
 * url: www.i-code.online
 * @author: anonyStar
 * @time: 2020/9/24 18:55
 */
public class RunnableDemo implements Runnable {
 
    @Override
    public void run() {
        //執行緒執行內容
        while (true){
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("RunnableDemo 執行緒正在執行,執行緒名:"+ Thread.currentThread().getName());
        }
    }
}

測驗代碼:

    @Test
    public void runnableTest(){
        // 本質還是 Thread ,這里直接 new Thread 類,傳入 Runnable 實作類
        Thread thread = new Thread(new RunnableDemo(),"runnable子執行緒 - 1");
        //啟動執行緒
        thread.start();

        while (true){
            System.out.println("這是main主執行緒:" + Thread.currentThread().getName());
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

運行結果:

1.3 實作 Callable 介面創建執行緒


  • 這種方式是通過 實作 Callable 介面,實作其中的 call 方法來實作執行緒,但是這種執行緒創建的方式是依賴于 ** **FutureTask **包裝器**來創建 Thread , 具體來看代碼


代碼如下:


/**
 * url: www.i-code.online
 * @author: anonyStar
 * @time: 2020/9/24 18:55
 */
public class CallableDemo implements Callable<String> {
    /**
     * Computes a result, or throws an exception if unable to do so.
     *
     * @return computed result
     * @throws Exception if unable to compute a result
     */
    @Override
    public String call() throws Exception {
        //執行緒執行內容
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("CallableDemo 執行緒正在執行,執行緒名:"+ Thread.currentThread().getName());

        return "CallableDemo 執行結束,,,,";
    }
}

測驗代碼:

    @Test
    public void callable() throws ExecutionException, InterruptedException {
        //創建執行緒池
        ExecutorService service = Executors.newFixedThreadPool(1);
        //傳入Callable實作同時啟動執行緒
        Future submit = service.submit(new CallableDemo());
        //獲取執行緒內容的回傳值,便于后續邏輯
        System.out.println(submit.get());
        //關閉執行緒池
        service.shutdown();
        //主執行緒
        System.out.println("這是main主執行緒:" + Thread.currentThread().getName());
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

結果:

有的時候,我們可能需要讓一步執行的執行緒在執行完成以后,提供一個回傳值給到當前的主執行緒,主執行緒需要依賴這個值進行后續的邏輯處理,那么這個時候,就需要用到帶回傳值的執行緒了



關于執行緒基礎知識的如果有什么問題的可以在網上查找資料學習學習!這里不再闡述

2. 執行緒的生命周期

  • Java 執行緒既然能夠創建,那么也勢必會被銷毀,所以執行緒是存在生命周期的,那么我們接下來從執行緒的生命周期開始去了解執行緒,

2.1 執行緒的狀態

2.1.1 執行緒六狀態認識


執行緒一共有 6 種狀態(NEW、RUNNABLE、BLOCKED、WAITING、TIME_WAITING、TERMINATED)

  • NEW:初始狀態,執行緒被構建,但是還沒有呼叫 start 方法

  • RUNNABLED:運行狀態,JAVA 執行緒把作業系統中的就緒和運行兩種狀態統一稱為“運行中”

  • BLOCKED:阻塞狀態,表示執行緒進入等待狀態, 也就是執行緒因為某種原因放棄了 CPU 使用權,阻塞也分為幾種情況

    • 等待阻塞:運行的執行緒執行 wait 方法,jvm 會把當前執行緒放入到等待佇列? 同步阻塞:運行的執行緒在獲取物件的同步鎖時,若該同步鎖被其他執行緒鎖占用了,那么 jvm 會把當前的執行緒放入到鎖池中
    • 其他阻塞:運行的執行緒執行 Thread.sleep 或者 t.join 方法,或者發出了 I/O 請求時,JVM 會把當前執行緒設定為阻塞狀態,當 sleep 結束、join 執行緒終止、io 處理完畢則執行緒恢復
  • TIME_WAITING:超時等待狀態,超時以后自動回傳

  • TERMINATED:終止狀態,表示當前執行緒執行完畢


2.1.2 代碼實操演示

  • 代碼:

    public static void main(String[] args) {
        ////TIME_WAITING 通過 sleep wait(time) 來進入等待超時中
        new Thread(() -> {
           while (true){
               //執行緒執行內容
               try {
                   TimeUnit.SECONDS.sleep(100);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
        },"Time_Waiting").start();
        //WAITING, 執行緒在 ThreadStatus 類鎖上通過 wait 進行等待
        new Thread(() -> {
            while (true){
                synchronized (ThreadStatus.class){
                    try {
                        ThreadStatus.class.wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Thread_Waiting").start();

        //synchronized 獲得鎖,則另一個進入阻塞狀態 blocked
        new Thread(() -> {
            while (true){
                synchronized(Object.class){
                    try {
                        TimeUnit.SECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Object_blocked_1").start();
        new Thread(() -> {
            while (true){
                synchronized(Object.class){
                    try {
                        TimeUnit.SECONDS.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        },"Object_blocked_2").start();
    }

啟動一個執行緒前,最好為這個執行緒設定執行緒名稱,因為這樣在使用 jstack 分析程式或者進行問題排查時,就會給開發人員提供一些提示

2.1.3 執行緒的狀態堆疊


? 運行該示例,打開終端或者命令提示符,鍵入“ jps ”, ( JDK1.5 提供的一個顯示當前所有 java 行程 pid 的命令)


? 根據上一步驟獲得的 pid ,繼續輸入 jstack pid (jstack是 java 虛擬機自帶的一種堆疊跟蹤工具,jstack 用于列印出給定的 java 行程 ID core file 或遠程除錯服務的 Java 堆疊資訊)

3. 執行緒的深入決議

3.1 執行緒的啟動原理

  • 前面我們通過一些案例演示了執行緒的啟動,也就是呼叫 start() 方法去啟動一個執行緒,當 run 方法中的代碼執行完畢以后,執行緒的生命周期也將終止,呼叫 start 方法的語意是當前執行緒告訴 JVM ,啟動呼叫 start 方法的執行緒,
  • 我們開始學習執行緒時很大的疑惑就是 啟動一個執行緒是使用 start 方法,而不是直接呼叫 run 方法,這里我們首先簡單看一下 start 方法的定義,在 Thread 類中
    public synchronized void start() {
        /**
         * This method is not invoked for the main method thread or "system"
         * group threads created/set up by the VM. Any new functionality added
         * to this method in the future may have to also be added to the VM.
         *
         * A zero status value corresponds to state "NEW".
         */
        if (threadStatus != 0)
            throw new IllegalThreadStateException();

        /* Notify the group that this thread is about to be started
         * so that it can be added to the group's list of threads
         * and the group's unstarted count can be decremented. */
        group.add(this);

        boolean started = false;
        try {
            //執行緒呼叫的核心方法,這是一個本地方法,native 
            start0();
            started = true;
        } finally {
            try {
                if (!started) {
                    group.threadStartFailed(this);
                }
            } catch (Throwable ignore) {
                /* do nothing. If start0 threw a Throwable then
                  it will be passed up the call stack */
            }
        }
    }
	
	//執行緒呼叫的 native 方法
    private native void start0();
  • 這里我們能看到 start 方法中呼叫了 native 方法 start0來啟動執行緒,這個方法是在 Thread 類中的靜態代碼塊中注冊的 , 這里直接呼叫了一個 native 方法 registerNatives
    /* Make sure registerNatives is the first thing <clinit> does. */
    private static native void registerNatives();
    static {
        registerNatives();
    }
  • 由于 registerNatives 方法是本地方法,我們要看其實作原始碼則必須去下載 jdk 原始碼,關于 jdk 及虛擬機 hotspot 的原始碼下載可以去 openJDK 官網下載 ,參考:

  • 我們可以本地查看原始碼或者直接去 http://hg.openjdk.java.net/jdk8u/jdk8u60/jdk/file/935758609767/src/share/native/java/lang/Thread.c 查看 Thread 類對應的本地方法 .c 檔案,

  • 如上圖,我們本地下載 jdk 工程,找到 src->share->native->java->lang->Thread.c 檔案

  • 上面是 Thread.c 中所有代碼,我們可以看到呼叫了 RegisterNatives 同時可以看到 method 集合中的映射,在呼叫本地方法 start0 時,實際呼叫了 JVM_StartThread ,它自身是由 c/c++ 實作的,這里需要在 虛擬機原始碼中去查看,我們使用的都是 hostpot 虛擬機,這個可以去 openJDK 官網下載,上述介紹了不再多說
  • 我們看到 JVM_StartThread 的定義是在 jvm.h 原始碼中,而 jvm.h 的實作則在虛擬機 hotspot 中,我們打開 hotspot 原始碼,找到 src -> share -> vm -> prims ->jvm.cpp 檔案,在 2955 行,可以直接檢索 JVM_StartThread , 方法代碼如下:

JVM_ENTRY(void, JVM_StartThread(JNIEnv* env, jobject jthread))
  JVMWrapper("JVM_StartThread");
  JavaThread *native_thread = NULL;

  bool throw_illegal_thread_state = false;

  {
    MutexLocker mu(Threads_lock);

    if (java_lang_Thread::thread(JNIHandles::resolve_non_null(jthread)) != NULL) {
      throw_illegal_thread_state = true;
    } else {
      // We could also check the stillborn flag to see if this thread was already stopped, but
      // for historical reasons we let the thread detect that itself when it starts running
      // <1> :獲取當前行程中執行緒的數量
      jlong size =
             java_lang_Thread::stackSize(JNIHandles::resolve_non_null(jthread));

      size_t sz = size > 0 ? (size_t) size : 0;

      // <2> :真正呼叫創建執行緒的方法
      native_thread = new JavaThread(&thread_entry, sz);
      if (native_thread->osthread() != NULL) {
        // Note: the current thread is not being used within "prepare".
        native_thread->prepare(jthread);
      }
    }
  }

  if (throw_illegal_thread_state) {
    THROW(vmSymbols::java_lang_IllegalThreadStateException());
  }

  assert(native_thread != NULL, "Starting null thread?");

  if (native_thread->osthread() == NULL) {
    // No one should hold a reference to the 'native_thread'.
    delete native_thread;
    if (JvmtiExport::should_post_resource_exhausted()) {
      JvmtiExport::post_resource_exhausted(
        JVMTI_RESOURCE_EXHAUSTED_OOM_ERROR | JVMTI_RESOURCE_EXHAUSTED_THREADS,
        "unable to create new native thread");
    }
    THROW_MSG(vmSymbols::java_lang_OutOfMemoryError(),
              "unable to create new native thread");
  }

  // <3> 啟動執行緒
  Thread::start(native_thread);

JVM_END

JVM_ENTRY 是用來定義 JVM_StartThread 函式的,在這個函式里面創建了一個真正和平臺有關的本地執行緒, 上述標記 <2> 處

  • 為了進一步執行緒創建,我們在進入 new JavaThread(&thread_entry, sz) 中查看一下具體實作程序,在 thread.cpp 檔案 1566 行處定義了 new 的方法

  • 對于上述代碼我們可以看到最終呼叫了 os::create_thread(this, thr_type, stack_sz); 來實作執行緒的創建,對于這個方法不同平臺有不同的實作,這里不再贅述,

  • 上面都是創建程序,之后再呼叫   Thread::start(native_thread); 在 JVM_StartThread 中呼叫,該方法的實作在 Thread.cpp

start 方法中有一個函式呼叫: os::start_thread(thread); ,呼叫平臺啟動執行緒的方法,最侄訓呼叫 Thread.cpp 檔案中的 JavaThread::run() 方法


3.2 執行緒的終止

3.2.1 通過標記位來終止執行緒

  • 正常我們執行緒內的東西都是回圈執行的,那么我們實際需求中肯定也存在想在其他執行緒來停止當前執行緒的需要,這是后我們可以通過標記位來實作,所謂的標記為其實就是 volatile 修飾的變數,著由它的可見性特性決定的,如下代碼就是依據 volatile 來實作標記位停止執行緒

    //定義標記為 使用 volatile 修飾
    private static volatile  boolean mark = false;

    @Test
    public void markTest(){
        new Thread(() -> {
            //判斷標記位來確定是否繼續進行
            while (!mark){
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("執行緒執行內容中...");
            }
        }).start();

        System.out.println("這是主執行緒走起...");
        try {
            TimeUnit.SECONDS.sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //10秒后將標記為設定 true 對執行緒可見,用volatile 修飾
        mark = true;
        System.out.println("標記位修改為:"+mark);
    }

3.2.2 通過 stop 來終止執行緒

  • 我們通過查看 Thread 類或者 JDK API 可以看到關于執行緒的停止提供了 stop() , supend() , resume() 等方法,但是我們可以看到這些方法都被標記了 @Deprecated 也就是過時的,
  • 雖然這幾個方法都可以用來停止一個正在運行的執行緒,但是這些方法都是不安全的,都已經被拋棄使用,所以在我們開發中我們要避免使用這些方法,關于這些方法為什么被拋棄以及導致的問題 JDK 檔案中較為詳細的描述 《Why Are Thread.stop, Thread.suspend, Thread.resume and Runtime.runFinalizersOnExit Deprecated?》
  • 在其中有這樣的描述:

  • 總的來說就是:

    • 呼叫 stop() 方法會立刻停止 run() 方法中剩余的全部作業,包括在 catchfinally 等陳述句中的內容,并拋出 ThreadDeath 例外(通常情況下此例外不需要顯示的捕獲),因此可能會導致一些作業的得不到完成,如檔案,資料庫等的關閉,
    • 呼叫 stop() 方法會立即釋放該執行緒所持有的所有的鎖,導致資料得不到同步,出現資料不一致的問題,

3.2.3 通過 interrupt 來終止執行緒

  • 通過上面闡述,我們知道了使用 stop 方法是不推薦的,那么我們用什么來更好的停止執行緒,這里就引出了 interrupt 方法,我們通過呼叫 interrupt 來中斷執行緒
  • 當其他執行緒通過呼叫當前執行緒的 interrupt 方法,表示向當前執行緒打個招呼,告訴他可以中斷執行緒的執行了,至于什么時候中斷,取決于當前執行緒自己
  • 執行緒通過檢查自身是否被中斷來進行相應,可以通過 isInterrupted() 來判斷是否被中斷,


我們來看下面代碼:

    public static void main(String[] args) {
        //創建 interrupt-1 執行緒

        Thread thread = new Thread(() -> {
            while (true) {
                //判斷當前執行緒是否中斷,
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("執行緒1 接收到中斷資訊,中斷執行緒...");
                    break;
                }
                System.out.println(Thread.currentThread().getName() + "執行緒正在執行...");

            }
        }, "interrupt-1");
        //啟動執行緒 1
        thread.start();

        //創建 interrupt-2 執行緒
        new Thread(() -> {
            int i = 0;
            while (i <20){
                System.out.println(Thread.currentThread().getName()+"執行緒正在執行...");
                if (i == 8){
                    System.out.println("設定執行緒中斷....");
                    //通知執行緒1 設定中斷通知
                    thread.interrupt();
                }
                i ++;
                try {
                    TimeUnit.MILLISECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        },"interrupt-2").start();
    }

列印結果如下:

上述代碼中我們可以看到,我們創建了 interrupt-1 執行緒,其中用 interrupt 來判斷當前執行緒是否處于中斷狀態,如果處于中斷狀態那么就自然結束執行緒,這里的結束的具體操作由我們開發者來決定,再創建 interrupt-2 執行緒,代碼相對簡單不闡述,當執行到某時刻時將執行緒 interrupt-1 設定為中斷狀態,也就是通知 interrupt-1 執行緒,


執行緒中斷標記復位 :

在上述 interrupt-1 代碼中如果加入 sleep 方法,那么我們會發現程式報出 InterruptedException 錯誤,同時,執行緒 interrupt-1 也不會停止,這里就是因為中斷標記被復位了 ,下面我們來介紹一下關于中斷標記復位相關的內容

  • 在執行緒類中提供了** **Thread.interrupted 的靜態方法,用來對執行緒中斷標識的復位,在上面的代碼中,我們可以做一個小改動,對 interrupt-1 執行緒創建的代碼修改如下:
        //創建 interrupt-1 執行緒

        Thread thread = new Thread(() -> {
            while (true) {
                //判斷當前執行緒是否中斷,
                if (Thread.currentThread().isInterrupted()) {
                    System.out.println("執行緒1 接收到中斷資訊,中斷執行緒...中斷標記:" + Thread.currentThread().isInterrupted());
                    Thread.interrupted(); // //對執行緒進行復位,由 true 變成 false
                    System.out.println("經過 Thread.interrupted() 復位后,中斷標記:" + Thread.currentThread().isInterrupted());
                    //再次判斷是否中斷,如果是則退出執行緒
                    if (Thread.currentThread().isInterrupted()) {
                        break;
                    }
                }
                System.out.println(Thread.currentThread().getName() + "執行緒正在執行...");

            }
        }, "interrupt-1");

上述代碼中 我們可以看到,判斷當前執行緒是否處于中斷標記為 true , 如果有其他程式通知則為 true 此時進入 if 陳述句中,對其進行復位操作,之后再次判斷,執行代碼后我們發現 interrupt-1 執行緒不會終止,而會一直執行

  • Thread.interrupted 進行執行緒中斷標記復位是一種主動的操作行為,其實還有一種被動的復位場景,那就是上面說的當程式出現 InterruptedException 例外時,則會將當前執行緒的中斷標記狀態復位,在拋出例外前, JVM 會將中斷標記 isInterrupted 設定為 false

在程式中,執行緒中斷復位的存在實際就是當前執行緒對外界中斷通知信號的一種回應,但是具體回應的內容有當前執行緒決定,執行緒不會立馬停止,具體是否停止等都是由當前執行緒自己來決定,也就是開發者,


3.3 執行緒終止 interrupt 的原理

  • 首先我們先來看一下在 Thread 中關于 interrupt 的定義:
    public void interrupt() {
        if (this != Thread.currentThread()) {
            checkAccess();  //校驗是否有權限來修改當前執行緒

            // thread may be blocked in an I/O operation
            synchronized (blockerLock) {
                Interruptible b = blocker;
                if (b != null) {
                    // <1> 呼叫 native 方法
                    interrupt0();  // set interrupt status
                    b.interrupt(this);
                    return;
                }
            }
        }

        // set interrupt status
        interrupt0();
    }
  • 上面代碼中我們可以看到,在 interrupt 方法中最終呼叫了 Native 方法 interrupt0 ,這里相關在執行緒啟動時說過,不再贅述,我們直接找到 hotspotjvm.cpp 檔案中 JVM_Interrupt 方法

  • JVM_Interrupt 方法比較簡單,其中我們可以看到直接呼叫了 Thread.cppinterrupt 方法,我們進入其中查看

  • 我們可以看到這里直接呼叫了  os::interrupt(thread) 這里是呼叫了平臺的方法,對于不同的平臺實作是不同的,我們這里如下所示,選擇 Linux 下的實作 os_linux.cpp 中,


在上面代碼中我們可以看到,在 1 處拿到 OSThread ,之后判斷如果 interruptfalse 則在 2 處呼叫 OSThreadset_interrupted 方法進行設定,我們可以進入看一下其實作,發現在 osThread.hpp 中定義了一個成員變數 volatile jint _interrupted;set_interrupted 方法其實就是將 _interrupted 設定為 true ,之后再通過 ParkEventunpark() 方法來喚醒執行緒,具體的程序在上面進行的簡單的注釋介紹,


歡迎關注公眾號“云棲簡碼”

本文由AnonyStar 發布,可轉載但需宣告原文出處,
仰慕「優雅編碼的藝術」 堅信熟能生巧,努力改變人生
歡迎關注微信公賬號 :云棲簡碼 獲取更多優質文章
更多文章關注筆者博客 :云棲簡碼

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

標籤:Java

上一篇:Java 將Html轉為PDF(二)

下一篇:mybatis_3CRUD操作

標籤雲
其他(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