天天看點

Java線程基礎操作程序和線程的概念使用線程

Java線程基礎操作程式和線程的概念使用線程

程序和線程的概念

程序 比較好了解,打開Windows 的任務管理器程序頁裡的一個個

exe

就可以了解為一個程序。

線程可以了解是在程序中獨立運作的子任務 ,具體見百度百科 https://baike.baidu.com/item/%E7%BA%BF%E7%A8%8B

使用線程

建立一個線程主要有兩種方式

  • 繼承 Thread 類
  • 實作 Runnable 接口
Note: Thread 類其實是實作了 Runnable 接口的。使用繼承 Thread 的方式建立線程時,最大的局限就是不支援多繼承,因為 Java 是單根繼承,為了支援多繼承,完全可以實作 Runnable 接口的方法。兩種方式在工作時的性質是一樣的,沒有本質的差別。

繼承 Thread 的方式

123456 public class MyThreadWithRunnable implements Runnable { @Override public void run() { System.out.println("--->"+this.getClass().getSimpleName()); }}

實作 Runnable 的方式

123456789101112 public class MyThreadWithThread extends Thread { public MyThreadWithThread() { super(); setName(this.getClass().getSimpleName()); } @Override public void run() { System.out.println("-->"+this.getName()); }}

啟動線程

1234567891011 public class Test { public static void main(String[] args){ MyThreadWithThread threadWithThread = new MyThreadWithThread(); MyThreadWithRunnable threadWithRunnable = new MyThreadWithRunnable(); Thread thread = new Thread(threadWithRunnable); System.out.println("---begin"); thread.start(); threadWithThread.start(); System.out.println("---stop"); }}
Note: 如果多次調用 start() 方法 會出現異常

Exception in thread "main" java.lang.IllegalThreadStateException

Console
1234 ---begin---stop-->MyThreadWithThread--->MyThreadWithRunnable
Java線程基礎操作程式和線程的概念使用線程

通過結果可以看到 代碼的結果和代碼的順序是不一樣的。

使用多線程時,代碼的運作結果與代碼執行順序或者調用順序是無關的。

線程是一個子任務,CPU 以不确定的方式,或者說以随機的時間來調用線程中的 run 方法。

資料共享和非線程安全

線程共享資料的情況就是多個線程通路同一個變量。

多個線程在通路同一個變量的時候會出現非線程安全問題。

非線程安全主要是指多個線程對同一個對象中的同一個執行個體變量進行操作時會出現值被更改、值不同步的情況,進而影響程式的執行流程。

可以通過給代碼上鎖的方式解決這個問題。在方法上加上 synchronized 關鍵字。

當一個線程調用一個方法前會先判斷這個方法有沒有上鎖,如果上鎖了說明有其他線程正在調用此方法。必須等其他線程對run()方法調用結束後才能執行run()。在等待的同時線程會不斷嘗試去拿這個鎖,而且是多個線程同時去拿,誰拿到誰 執行。這樣就實作了排隊調用run()方法。

synchronized 可以在任意對象及方法上枷鎖,而加鎖的這段代碼成為“互斥區”或者“臨界區”。

常用方法

  • Thread.currentThread() 目前代碼運作所在的線程
  • getName() 目前線程的名字
  • isAlive() 測試目前線程是否處于活動狀态。什麼是活動狀态呢?活動狀态就是線程已經啟動且尚未終止。線程處于正在運作或準備開始運作的狀态,就是認為線程是“存活”的。
  • sleep() 作用是在指定的毫秒數内讓目前“正在執行的線程”休眠(暫停執行)。這個正在執行的線程就是 “Thread.currentThread()” 傳回的線程
  • getId() 作用是放回線程唯一id

停止線程

停止線程意味着線上程處理完任務之前停掉正在做的操作,也就是放棄目前的操作。

停止一個線程可以使用 Thread.stop()方法,但最好不用它,雖然它可以停止一個正在運作的線程,但是這個方法是不安全的,而且已經被廢棄。

大多數停止一個線程的操作使用 Thread.interrupt()方法,盡管方法的名稱是“停止,終止”的意思,當這個方法不會終止一個正在運作的線程,還需要加入一個判斷才能完成線程的停止。

Java中有三種方法可以停止正在運作的線程

  1. 使用退出标志,使線程正常退出,也就是當run方法完成後線程終止
  2. 使用 stop 方法強行終止線程,但是不推薦使用,因為 stop 和 suspend 及 resume 一樣,都是廢棄過期的方法,使用它們産生不可預料的後果
  3. 使用 interrupt 停止線程

判斷線程是否停止

  1. Thread.interrupted()(靜态方法) 作用是測試目前線程是否已經中斷 執行後将停止狀态清除為false
  2. isInterrupted() 作用是測試線程是否已經中斷。執行後不會清除狀态标志。
先看一下

Thread.interrupted()

1234567891011121314151617181920 /** * Tests whether the current thread has been interrupted. The * <i>interrupted status</i> of the thread is cleared by this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if the current thread has been interrupted; * <code>false</code> otherwise. * @see #isInterrupted() * @revised 6.0 */public static boolean interrupted() { return currentThread().isInterrupted(true);}

測試目前線程是否已經中斷。 線程的 中斷狀态 通過此方法清除。換句話說就是,這個方法入如果連續兩次調用,第二次将會傳回 false(在第一次調用已清除了其中斷狀态後,且第二次調用檢驗完中斷狀态前,目前線程再次中斷的情況除外)

線程中斷被忽略是因為沒有在存活的時候中斷,這個方法将會傳回 false

如果目前線程已經被中斷了 将會傳回 true

再看一下

isInterrupted

12345678910111213141516 /** * Tests whether this thread has been interrupted. The <i>interrupted * status</i> of the thread is unaffected by this method. * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if this thread has been interrupted; * <code>false</code> otherwise. * @see #interrupted() * @revised 6.0 */public boolean isInterrupted() { return isInterrupted(false);}

測試該線程是否已經中斷。線程的中斷狀态不受此方法影響。

可以看到兩個方法的源碼都是調用了

isInterrupted()

不同的是

interrupted()

是先調用

currentThread()

擷取到目前代碼運作所在的線程。然後讓目前線程調用

isInterrupted()

。而

isInterrupted

這是直接調用,是以判斷的是這個執行個體線程的狀态。

再看一下

isInterrupted()

123456 /** * Tests if some Thread has been interrupted. The interrupted state * is reset or not based on the value of ClearInterrupted that is * passed. */private native boolean isInterrupted(boolean ClearInterrupted);

測試某些線程是否被中斷。中斷狀态根據傳遞的ClearInterrupted的值決定重置或不重置。

Thread.interrupted()

傳入了 false 是以會重置中斷狀态。

異常法停止

這裡的所謂異常法停止就是對你想中斷的線程調用

interrupt()

打上中斷辨別。在你執行操作的線程中一定要在某個地方檢測 中斷狀态 如果中斷狀态為 true 了就停止操作。

12345678910111213141516171819202122232425 public class MyThread extends Thread { public MyThread(String name) { super(name); } @Override public void run() { try { System.out.println(Thread.currentThread().getName()+"---》begin"); for (int i=0;i<500000;i++){ System.out.println(Thread.currentThread().getName()+"-->"+i); if (this.isInterrupted()){ System.out.println("檢測到目前線程執行個體中斷标志("+this.getName()+")-->"+this.isInterrupted()); throw new InterruptedException(); } } System.out.println("for循環後執行-目前線程執行個體("+this.getName()+")中斷辨別->"+this.isInterrupted()+";目前代碼運作線程("+Thread.currentThread().getName()+")中斷标志--》"+Thread.interrupted()); System.out.println(Thread.currentThread().getName()+"----end--->"); } catch (InterruptedException e) { e.printStackTrace(); System.out.println("異常法中斷--》"+e.getMessage()); } }}

測試

12345678910111213 public class Test { public static void main(String[]args) throws InterruptedException { MyThread myThread =new MyThread("myThread"); myThread.start(); Thread.sleep(50); System.out.println("中斷myThread"); myThread.interrupt(); Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+" ====end--->"+myThread.isInterrupted()); }

Console

12345678910111213141516171819202122 .....myThread-->7665myThread-->7666myThread-->7667myThread-->7668myThread-->7669myThread-->7670myThread-->7671myThread-->7672myThread-->7673myThread-->7674myThread-->7675myThread-->7676中斷myThreadmyThread-->7677檢測到目前線程執行個體中斷标志(myThread)-->true異常法中斷--》nulljava.lang.InterruptedException at com.skymxc.example.multithreading.stop.MyThread.run(MyThread.java:17)main ====end--->falseProcess finished with exit code 0

當然,這裡是用的抛出異常的方法強行中斷。也可使用 break 然後繼續一個收尾工作。

關于 stop()方法 參考這篇文章 https://blog.csdn.net/jiangwei0910410003/article/details/19900007

暫停線程

暫停線程意味着還可以恢複線程的執行,在 Java 中使用

suspend()

暫停線程的執行,使用

ressume()

恢複線程的執行。

用一個例子看看這兩個方法怎麼用
123456789101112131415161718192021222324252627282930 public class CountThread extends Thread { public CountThread() { super("countThread"); } private int i; public int getI() { return i; } public void setI(int i) { this.i = i; } @Override public void run() { SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); while (true){ i++;// System.out.println(dateFormat.format(new Date())+"--->"+i++); if (this.isInterrupted()){ break; } } System.out.println(dateFormat.format(new Date())+"---停止--》"+this.getName()); }}

測試

1234567891011121314151617181920212223242526272829303132 public class SuspendTest { public static void main(String[]args) throws InterruptedException { SimpleDateFormat dateFormat = new SimpleDateFormat("HH:mm:ss.SSS"); CountThread countThread = new CountThread(); countThread.start(); Thread.sleep(1000); //階段 A 1000ms 後 countThread.suspend(); System.out.println( dateFormat.format(new Date())+"--A->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); Thread.sleep(1000); //階段B 1000ms 後 System.out.println( dateFormat.format(new Date())+"--B->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); countThread.resume(); Thread.sleep(1000); //階段C countThread.suspend(); System.out.println( dateFormat.format(new Date())+"--C->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); Thread.sleep(1000); countThread.resume(); Thread.sleep(1000); countThread.interrupt(); //階段D Thread.sleep(1000); System.out.println( dateFormat.format(new Date())+"--D->"+countThread.getI()+";isInterrupted-->"+countThread.isInterrupted()); }}

Console

1234567 17:48:11.934--A->516318612;isInterrupted-->false17:48:12.935--B->516318612;isInterrupted-->false17:48:13.935--C->1043644966;isInterrupted-->false17:48:15.935---停止--》countThread17:48:16.935--D->1565065219;isInterrupted-->falseProcess finished with exit code 0

确實達到了暫停和恢複的目的。

suspend()和 resmue() 的缺點—-獨占

如果你在上面的 CountThread 中列印 i 你就會發現一個問題 在main 線程的 列印都沒出來,而且程式已知在運作,沒有結束,也沒有log。

先看一下 println() 的實作

12345678910111213 /** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>String</code> to be printed. */ public void println(String x) { synchronized (this) { print(x); newLine(); } }

可以看到方法裡 使用 synchronized 鎖住了目前對象。

在例子中我們讓 countThread 暫停,雖然它确實暫停了,但是沒有釋放鎖,且一直在占着,這樣的結果就是我們在main 線程的 列印一直在等鎖,且一直等不到。

雖然 suspend()方法 已經被廢棄,但是了解它為什麼被廢棄還是很有意義的。

suspend()和 resmue() 的缺點—-不同步

因為它的獨占 是以無法使用 synchronized ,也就無法保證資料同步

priority 線程的優先級

優先級較高的線程得到的CPU資源較多,也就是CPU優先執行級别較高的線程對象中的任務。

使用

setPriority()

方法設定線程優先級

12345678910111213 public final void setPriority(int newPriority) { ThreadGroup g; checkAccess(); if (newPriority > MAX_PRIORITY || newPriority < MIN_PRIORITY) { throw new IllegalArgumentException(); } if((g = getThreadGroup()) != null) { if (newPriority > g.getMaxPriority()) { newPriority = g.getMaxPriority(); } setPriority0(priority = newPriority); } }

優先級 分為 1 ~ 10 這10個等級。如果不在範圍内就會抛出異常。

JDK預設了三個優先級的值:

1234567891011121314 /** * The minimum priority that a thread can have. */ public final static int MIN_PRIORITY = 1; /** * The default priority that is assigned to a thread. */ public final static int NORM_PRIORITY = 5; /** * The maximum priority that a thread can have. */ public final static int MAX_PRIORITY = 10;
  • 線程優先級的繼承特性

    在 Java 中,線程的優先級具有繼承性,例如A線程啟動B線程,則B線程的優先級與A是一樣的。

  • 優先級具有規則性

    CPU會盡量将執行資源讓給優先級較高的線程。

  • 優先級具有随機性

    雖然CPU會盡量讓優先級高的線程得到更多的資源,優先級高的線程不一定每一次都先執行完 run() 方法中的任務,不要把優先級和執行順序及結果挂鈎。

end