線程類有好多方法,下面介紹一些經常使用的方法。java
啓動一個線程用的是thread.start()方法,若是直接調用run方法是同步調用,至關於一個普通的方法調用。面試
start()方法使線程開始執行,JVM會自動調用線程的run方法。new出來線程,調用start()方法即處於RUNNABLE(可運行)狀態了。處於RUNNABLE狀態的線程可能正在Java虛擬機中運行,也可能正在等待處理器的資源,由於一個線程必須得到CPU的資源後,才能夠運行其run()方法中的內容,不然排隊等待。算法
/** * Causes this thread to begin execution; the Java Virtual Machine * calls the <code>run</code> method of this thread. * <p> * The result is that two threads are running concurrently: the * current thread (which returns from the call to the * <code>start</code> method) and the other thread (which executes its * <code>run</code> method). * <p> * It is never legal to start a thread more than once. * In particular, a thread may not be restarted once it has completed * execution. * * @exception IllegalThreadStateException if the thread was already * started. * @see #run() * @see #stop() */ public synchronized void start() { if (threadStatus != 0) throw new IllegalThreadStateException(); group.add(this); boolean started = false; try { start0(); started = true; } finally { try { if (!started) { group.threadStartFailed(this); } } catch (Throwable ignore) { } } }
根據線程的狀態來判斷是否已經調用其start()方法, threadStatus 能夠保證只調用一次start,屢次調用會報錯。而且在start()方法中調用了一個start0()方法,start0()是一個native方法。數據庫
/* Java thread status for tools, * initialized to indicate thread 'not yet started' */ private volatile int threadStatus = 0;
private native void start0();
兩次調用start()方法會報錯:app
package cn.qlq.thread.one; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class SubThread extends Thread { private static final Logger log = LoggerFactory.getLogger(SubThread.class); @Override public void run() { log.debug("subThread run,threadname->{}", Thread.currentThread().getName()); } public static void main(String[] args) { SubThread subThread = new SubThread(); subThread.start(); subThread.start(); log.debug("運行結束,threadname->{}", Thread.currentThread().getName()); } }
結果:jvm
Exception in thread "main" java.lang.IllegalThreadStateException
at java.lang.Thread.start(Thread.java:705)
at cn.qlq.thread.one.SubThread.main(SubThread.java:18)
2018-12-05 14:04:26 [cn.qlq.thread.one.SubThread]-[DEBUG] subThread run,threadname->Thread-0ide
/** * Returns a reference to the currently executing thread object. * * @return the currently executing thread. */ public static native Thread currentThread();
currentThread返回當前代碼段正在被哪一個調用的線程。例如:測試
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究靜態方法currentThread * * @author Administrator * */ public class CurrentThreadMethodDemo { private static final Logger log = LoggerFactory.getLogger(CurrentThreadMethodDemo.class); public static void main(String[] args) { Thread currentThread = Thread.currentThread(); log.debug("currentThread -> {}", currentThread); log.debug("currentThreadName -> {}", currentThread.getName()); } }
結果:this
2018-12-05 16:07:51 [cn.qlq.thread.two.CurrentThreadMethodDemo]-[DEBUG] currentThread -> Thread[main,5,main]
2018-12-05 16:07:51 [cn.qlq.thread.two.CurrentThreadMethodDemo]-[DEBUG] currentThreadName -> mainspa
查看Thread.toString()的源碼返回的是線程的名稱+優先級+所屬組的名稱:
public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } }
/** * Tests if this thread is alive. A thread is alive if it has * been started and has not yet died. * * @return <code>true</code> if this thread is alive; * <code>false</code> otherwise. */ public final native boolean isAlive();
isAlive方法用於判斷當前線程是否處於活動狀態。什麼是活動狀態呢?活動狀態就是已經啓動還沒有終止的,線程處於正在運行或者準備開始運行的狀態就認爲線程是"活動"的。(新建狀態的線程isAlive()返回的是false)
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究isAlive方法 * * @author Administrator * */ public class IsAliveMethodDemo { private static final Logger log = LoggerFactory.getLogger(IsAliveMethodDemo.class); public static void main(String[] args) { Thread r1 = new Thread() { @Override public void run() { log.debug("run isAlive->{}", this.isAlive()); } }; log.debug("begain---r1 isAlive->{}", r1.isAlive()); r1.start(); log.debug("end---r1 isAlive->{}", r1.isAlive()); } }
結果:
2018-12-05 16:25:34 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] begain---r1 isAlive->false
2018-12-05 16:25:34 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] end---r1 isAlive->true
2018-12-05 16:25:34 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] run isAlive->true
須要說明以下代碼:
log.debug("end---r1 isAlive->{}", r1.isAlive());
雖然在上面打印的結果是true,可是此值是不肯定的。打印true是由於r1尚未執行完畢,將上面代碼修改成下面:
log.debug("begain---r1 isAlive->{}", r1.isAlive()); r1.start(); Thread.sleep(1 * 1000); log.debug("end---r1 isAlive->{}", r1.isAlive());
結果:
2018-12-05 16:34:19 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] begain---r1 isAlive->false
2018-12-05 16:34:19 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] run isAlive->true
2018-12-05 16:34:20 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] end---r1 isAlive->false
再次修改代碼run方法中休眠10秒鐘:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究isAlive方法 * * @author Administrator * */ public class IsAliveMethodDemo { private static final Logger log = LoggerFactory.getLogger(IsAliveMethodDemo.class); public static void main(String[] args) throws InterruptedException { Thread r1 = new Thread() { @Override public void run() { try { Thread.sleep(1 * 10000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("run isAlive->{}", this.isAlive());// F } }; log.debug("begain---r1 isAlive->{}", r1.isAlive());// T r1.start(); log.debug("end---r1 isAlive->{}", r1.isAlive());// T log.debug("finish"); } }
結果:
2018-12-05 17:08:52 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] begain---r1 isAlive->false
2018-12-05 17:08:52 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] end---r1 isAlive->true
2018-12-05 17:08:52 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] finish
2018-12-05 17:09:02 [cn.qlq.thread.two.IsAliveMethodDemo]-[DEBUG] run isAlive->true
咱們在主線程中執行r1.isAlive的時候會等待r1線程休眠事後纔打印run方法中的isAlive。也就是說當一個Thread休眠以後,會繼續執行休眠以後的代碼。
/** * Causes the currently executing thread to sleep (temporarily cease * execution) for the specified number of milliseconds, subject to * the precision and accuracy of system timers and schedulers. The thread * does not lose ownership of any monitors. * * @param millis * the length of time to sleep in milliseconds * * @throws IllegalArgumentException * if the value of {@code millis} is negative * * @throws InterruptedException * if any thread has interrupted the current thread. The * <i>interrupted status</i> of the current thread is * cleared when this exception is thrown. */ public static native void sleep(long millis) throws InterruptedException;
方法sleep()的做用是在指定的毫秒數內讓當前"正在執行的線程"休眠(暫停執行)。 這個"正在執行的線程"是指Thread.currentThread()返回的線程。可是sleep不會釋放鎖。(The thread does not lose ownership of any monitors.)
sleep(long)使當前線程進入超時等待(TIMED_WAITING)狀態,因此執行sleep()的線程在指定的時間內確定不會被執行;
sleep(long)可以使優先級低的線程獲得執行的機會,固然也可讓同優先級的線程有執行的機會;
sleep(long)是不會釋放鎖標誌的。
會拋出中斷異常
測試代碼:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究sleep方法 * * @author Administrator * */ public class SleepMethodDemo { private static final Logger log = LoggerFactory.getLogger(SleepMethodDemo.class); public static void main(String[] args) throws InterruptedException { Thread r1 = new Thread() { @Override public void run() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("over sleep,{}", System.currentTimeMillis()); } }; log.debug("begain->{}", System.currentTimeMillis()); r1.start(); log.debug("end---r1 isAlive->{}", r1.isAlive()); log.debug("end->{}", System.currentTimeMillis()); } }
結果:
2018-12-05 17:27:59 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] begain->1544002079805
2018-12-05 17:27:59 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] end---r1 isAlive->true
2018-12-05 17:27:59 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] end->1544002079808
2018-12-05 17:28:09 [cn.qlq.thread.two.SleepMethodDemo]-[DEBUG] over sleep,1544002089808
補充:一道經典的面試題:
Thread.sleep(0)的做用是什麼?
因爲Java採用搶佔式的線程調度算法,所以可能會出現某條線程經常獲取到CPU控制權的狀況,爲了讓某些優先級比較低的線程也能獲取到CPU控制權,可使用Thread.sleep(0)手動觸發一次操做系統分配時間片的操做,這也是平衡CPU控制權的一種操做。
/** * Returns the identifier of this Thread. The thread ID is a positive * <tt>long</tt> number generated when this thread was created. * The thread ID is unique and remains unchanged during its lifetime. * When a thread is terminated, this thread ID may be reused. * * @return this thread's ID. * @since 1.5 */ public long getId() { return tid; }
此方法返回線程的惟一表示,是一個long型的正數,在線程建立的時候被賦值。
其生成方式是一個靜態成員變量一直在自增,而且自增方法也加了同步鎖。也就是說線程的ID是0開始一直自增。
/* For generating thread ID */ private static long threadSeqNumber; private static synchronized long nextThreadID() { return ++threadSeqNumber; }
補充:順便提一下name的生成方式:(Thread-0,默認是線程名稱是Thread-加上一個自增的數, threadInitNumber用於name的自增)
/* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; } public Thread() { init(null, null, "Thread-" + nextThreadNum(), 0); } private void init(ThreadGroup g, Runnable target, String name, long stackSize) { init(g, target, name, stackSize, null); }
測試代碼:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究getId * * @author Administrator * */ public class GetIdMethodDemo extends Thread { private static final Logger log = LoggerFactory.getLogger(GetIdMethodDemo.class); @Override public void run() { log.debug("threadName->{},getId->{}", Thread.currentThread().getName(), Thread.currentThread().getId()); } public static void main(String[] args) throws InterruptedException { log.debug("threadName->{},getId->{}", Thread.currentThread().getName(), Thread.currentThread().getId()); for (int i = 0; i < 10; i++) { GetIdMethodDemo t1 = new GetIdMethodDemo(); t1.start(); } } }
結果:
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->main,getId->1
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-0,getId->9
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-1,getId->10
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-2,getId->11
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-3,getId->12
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-5,getId->14
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-4,getId->13
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-8,getId->17
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-6,getId->15
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-9,getId->18
2018-12-05 [cn.qlq.thread.two.GetIdMethodDemo]-[DEBUG] threadName->Thread-7,getId->16
main線程的ID爲1.其他線程名稱後的默認序號是正常的遞增,可是ID倒是缺失了中間的2-8,這也是一直跟代碼沒有跟出來。。。。。。。(此處有疑問-----答案在最後補充中得以證明)
/** * A hint to the scheduler that the current thread is willing to yield * its current use of a processor. The scheduler is free to ignore this * hint. * * <p> Yield is a heuristic attempt to improve relative progression * between threads that would otherwise over-utilise a CPU. Its use * should be combined with detailed profiling and benchmarking to * ensure that it actually has the desired effect. * * <p> It is rarely appropriate to use this method. It may be useful * for debugging or testing purposes, where it may help to reproduce * bugs due to race conditions. It may also be useful when designing * concurrency control constructs such as the ones in the * {@link java.util.concurrent.locks} package. */ public static native void yield();
此方法的做用是放棄當前的CPU資源,將它讓給其餘的任務去佔用CPU的時間。可是放棄的時間不肯定,有可能剛剛放棄就立刻得到CPU時間片。
sleep 方法使當前運行中的線程睡眠一段時間,進入超時等待狀態,這段時間的長短是由程序設定的,yield方法使當前線程讓出CPU佔有權,但讓出的時間是不可設定的。
yield()也不會釋放鎖標誌。
yield()只是使當前線程從新回到可運行狀態,因此執行yield()的線程有可能在進入到可執行狀態後立刻又被執行。
yield()只能使同優先級或更高優先級的線程有執行的機會。
實際上,yield()方法對應了以下操做;先檢測當前是否有相同優先級的線程處於同可運行狀態,若有,則把CPU的佔有權交給次線程,不然繼續運行原來的線程,因此yield()方法稱爲「退讓」,它把運行機會讓給了同等級的其餘線程。
測試代碼:
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class YieldMethodDemo extends Thread { private static final Logger log = LoggerFactory.getLogger(YieldMethodDemo.class); @Override public void run() { long begainTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 5000000; i++) { Thread.yield(); count = count + (i + 1); } long endTime = System.currentTimeMillis(); log.debug("用時:{}毫秒", endTime - begainTime); } public static void main(String[] args) { YieldMethodDemo tDemo = new YieldMethodDemo(); tDemo.start(); } }
結果:
2018-12-05 21:23:40 [cn.qlq.thread.two.YieldMethodDemo]-[DEBUG] 用時:6392毫秒
將上面Thread.yield()註釋掉再次測試:
結果:
2018-12-05 21:27:26 [cn.qlq.thread.two.YieldMethodDemo]-[DEBUG] 用時:14毫秒
join方法的主要做用就是同步,它可使得線程之間的並行執行變爲串行執行。在A線程中調用了B線程的join()方法時,表示只有當B線程執行完畢時,A線程才能繼續執行。
join方法中若是傳入參數,則表示這樣的意思:若是A線程中掉用B線程的join(10),則表示A線程會等待B線程執行10毫秒,10毫秒事後,A、B線程並行執行。須要注意的是,jdk規定,join(0)的意思不是A線程等待B線程0秒,而是A線程等待B線程無限時間,直到B線程執行完畢,即join(0)等價於join()。
join方法必須在線程start方法調用以後調用纔有意義。這個也很容易理解:若是一個線程都沒有start,那它也就沒法同步了。
源碼以下:
/** * Waits for this thread to die.*/ public final void join() throws InterruptedException { join(0); }
public final synchronized void join(long millis) throws InterruptedException { long base = System.currentTimeMillis(); long now = 0; if (millis < 0) { throw new IllegalArgumentException("timeout value is negative"); } if (millis == 0) { while (isAlive()) { wait(0); } } else { while (isAlive()) { long delay = millis - now; if (delay <= 0) { break; } wait(delay); now = System.currentTimeMillis() - base; } } }
測試代碼:
package cn.qlq.thread.two; /** * 原生的線程類Thread的使用方法 * * @author Administrator * */ public class JoinMethod extends Thread { /** * 更改線程名字 * * @param threadName */ public JoinMethod(String threadName) { this.setName(threadName); } @Override public void run() { for (int i = 0; i < 5; i++) { try { Thread.sleep(1 * 500); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "-----" + i); } } public static void main(String[] args) { JoinMethod t1 = new JoinMethod("t1"); JoinMethod t2 = new JoinMethod("t2"); JoinMethod t3 = new JoinMethod("t3"); t1.start(); /** * join的意思是使得放棄當前線程的執行,並返回對應的線程,例以下面代碼的意思就是: * 程序在main線程中調用t1線程的join方法,則main線程放棄cpu控制權,並返回t1線程繼續執行直到線程t1執行完畢 * 因此結果是t1線程執行完後,纔到主線程執行,至關於在main線程中同步t1線程,t1執行完了,main線程纔有執行的機會 */ try { t1.join(); } catch (InterruptedException e) { e.printStackTrace(); } if (t2.isAlive()) { System.out.println("t2 is alive"); } else { System.out.println("t2 is not alive"); } t2.start(); t3.start(); } }
結果:
t1-----0
t1-----1
t1-----2
t1-----3
t1-----4
t2 is not alive
t2-----0
t3-----0
t2-----1
t3-----1
t2-----2
t3-----2
t2-----3
t3-----3
t2-----4
t3-----4
在操做系統中,線程能夠劃分優先級,優先級較高的線程能夠得到更多的CPU資源,也就是CPU優先執行優先級較高的線程對象中的任務。
設置線程優先級有助於幫"線程規劃器"肯定在下一次選擇哪個線程來優先執行。
源碼以下:
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); } }
在Java中,線程的優先級分爲1-10,小於1或者大於10JDK會拋出IllegalArgumentException異常
/** * 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;
(1)線程優先級具備繼承性,若是A線程建立B線程,則B線程的優先級與A線程是同樣的
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 線程優先級的方法 * * @author QiaoLiQiang * @time 2018年12月5日下午10:44:25 */ public class PriorityMethod extends Thread { private static final Logger log = LoggerFactory.getLogger(PriorityMethod.class); @Override public void run() { Runnable r1 = new Runnable() { @Override public void run() { try { Thread.sleep(1 * 100); } catch (InterruptedException e) { e.printStackTrace(); } } }; Thread thread = new Thread(r1); thread.start(); log.debug("thread.getPriority->{}", thread.getPriority()); } public static void main(String[] args) { // 線程裏面建立線程 PriorityMethod priorityMethod = new PriorityMethod(); priorityMethod.setPriority(9); priorityMethod.start(); } }
結果:
2018-12-05 22:52:50 [cn.qlq.thread.two.PriorityMethod]-[DEBUG] thread.getPriority->9
(2)優先級具備規則性--高優先級的線程老是大部分先執行完
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 線程優先級的方法(高優先級的大部分先執行完) * * @author QiaoLiQiang * @time 2018年12月5日下午10:44:25 */ public class PriorityMethod2 extends Thread { private static final Logger log = LoggerFactory.getLogger(PriorityMethod2.class); public PriorityMethod2(int privority) { this.setPriority(privority); } @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1 * 100); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("[end]thread.getPriority->{}", this.getPriority()); } public static void main(String[] args) { // 線程裏面建立線程 PriorityMethod2 priorityMethod1 = new PriorityMethod2(9); PriorityMethod2 priorityMethod2 = new PriorityMethod2(9); PriorityMethod2 priorityMethod3 = new PriorityMethod2(9); PriorityMethod2 priorityMethod4 = new PriorityMethod2(2); PriorityMethod2 priorityMethod5 = new PriorityMethod2(2); PriorityMethod2 priorityMethod6 = new PriorityMethod2(2); PriorityMethod2 priorityMethod7 = new PriorityMethod2(5); PriorityMethod2 priorityMethod8 = new PriorityMethod2(5); PriorityMethod2 priorityMethod9 = new PriorityMethod2(5); priorityMethod1.start(); priorityMethod2.start(); priorityMethod3.start(); priorityMethod4.start(); priorityMethod5.start(); priorityMethod6.start(); priorityMethod7.start(); priorityMethod8.start(); priorityMethod9.start(); } }
結果:
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->5
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->5
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->5
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->2
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->2
2018-12-05 23:01:25 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->2
(3)優先級具備隨機性
隨機性是指不必定每次都是優先級高的線程先執行完。線程的執行具備不肯定性和隨機性。尤爲是當優先級接近的時候更是具備不肯定性。
package cn.qlq.thread.two; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 線程優先級的方法(高優先級的大部分先執行完) * * @author QiaoLiQiang * @time 2018年12月5日下午10:44:25 */ public class PriorityMethod2 extends Thread { private static final Logger log = LoggerFactory.getLogger(PriorityMethod2.class); public PriorityMethod2(int privority) { this.setPriority(privority); } @Override public void run() { for (int i = 0; i < 3; i++) { try { Thread.sleep(1 * 100); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug("[end]thread.getPriority->{}", this.getPriority()); } public static void main(String[] args) { // 線程裏面建立線程 PriorityMethod2 priorityMethod1 = new PriorityMethod2(9); PriorityMethod2 priorityMethod2 = new PriorityMethod2(9); PriorityMethod2 priorityMethod3 = new PriorityMethod2(9); PriorityMethod2 priorityMethod4 = new PriorityMethod2(8); PriorityMethod2 priorityMethod5 = new PriorityMethod2(8); PriorityMethod2 priorityMethod6 = new PriorityMethod2(8); PriorityMethod2 priorityMethod7 = new PriorityMethod2(7); PriorityMethod2 priorityMethod8 = new PriorityMethod2(7); PriorityMethod2 priorityMethod9 = new PriorityMethod2(7); priorityMethod1.start(); priorityMethod2.start(); priorityMethod3.start(); priorityMethod4.start(); priorityMethod5.start(); priorityMethod6.start(); priorityMethod7.start(); priorityMethod8.start(); priorityMethod9.start(); } }
結果:
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->8
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->7
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->8
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->8
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->7
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->7
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
2018-12-05 23:03:18 [cn.qlq.thread.two.PriorityMethod2]-[DEBUG] [end]thread.getPriority->9
在Java中有兩種線程,一種是守護線程,一種是用戶線程。
守護線程是一種特殊的線程,當進程中不存在非守護線程了,則守護線程自動銷燬。典型的守護線程就是垃圾回收線程,當進程中沒有非守護線程也就沒有垃圾回收的必要了。
守護線程的做用就是爲其餘線程的運行提供便利,最典型的例子是GC線程。
關於守護線程應該注意:
(1) thread.setDaemon(true)必須在thread.start()以前設置,不然會跑出一個IllegalThreadStateException異常。你不能把正在運行的常規線程設置爲守護線程。
(2) 在Daemon線程中產生的新線程也是Daemon的。新建一個線程默認是用戶線程。
(3) 守護線程應該永遠不去訪問固有資源,如文件、數據庫,由於它會在任什麼時候候甚至在一個操做的中間發生中斷
package cn.qlq.thread.two; /** * 守護線程 * * @author QiaoLiQiang * @time 2018年12月5日下午11:09:49 */ public class DaemonThread extends Thread { private int i; @Override public void run() { while (true) { System.out.println(i++); try { Thread.sleep(1 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) throws InterruptedException { DaemonThread daemonThread = new DaemonThread(); daemonThread.setDaemon(true); daemonThread.start(); System.out.println(daemonThread.isDaemon()); Thread.sleep(5000); System.out.println("我離開後daemonThread也結束打印"); } }
結果:(由於主線程退出以後沒有任何用戶線程,因此守護線程也就結束了-------全部用戶線程結束,因此守護線程也結束,對應的進程也就結束了)
true
0
1
2
3
4
我離開後daemonThread也結束打印
補充:關於守護線程和非守護線程更正確的認識是:
其實守護線程和非守護線程同樣,只是若是是守護線程,在沒有用戶線程的狀況下會結束(全部用戶線程的結束會致使守護線程的結束,進而致使進程的結束);而若是是用戶線程的話只有在線程完結的狀況下才會結束。
例如:上面的代碼中將設爲守護線程的代碼注掉,繼續執行:
結果:(主線程結束可是仍然在打印,由於daemonThread是一個用戶線程------雖然主線程結束,可是存在非守護,因此守護線程不會結束,進程也不會結束)
查看進程的線程信息,發現不存在main線程(main線程已經結束---這裏也證實主線程的結束不會影響其餘用戶線程)
補充:守護線程run執行完畢也會結束線程,並非說守護線程就一直存活
package cn.qlq.thread.two; /** * 守護線程 * * @author QiaoLiQiang * @time 2018年12月5日下午11:09:49 */ public class DaemonThread extends Thread { private int i; @Override public void run() { System.out.println(i++); } public static void main(String[] args) throws InterruptedException { DaemonThread daemonThread = new DaemonThread(); daemonThread.setDaemon(true); daemonThread.start(); while (true) { } } }
jvisualvm查看線程信息: (守護線程run執行完因此也正常結束)
其餘還有獲取線程名稱,線程組的方法等方法。
package cn.qlq.thread.two; import java.util.Map; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * 研究其餘方法的使用 * * @author QiaoLiQiang * @time 2018年12月5日下午10:21:16 */ public class OtherMethod extends Thread { private static final Logger log = LoggerFactory.getLogger(OtherMethod.class); @Override public void run() { try { Thread.sleep(10 * 1000); } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { OtherMethod otherMethod = new OtherMethod(); otherMethod.start(); log.debug("getName()->{}", otherMethod.getName());// 獲取名稱 log.debug("getPriority()->{}", otherMethod.getPriority());// 獲取優先級 ThreadGroup threadGroup = otherMethod.getThreadGroup();// 獲取線程組 log.debug("threadGroup - > {}", threadGroup); // 修改優先級 otherMethod.setPriority(9); log.debug("getPriority()->{}", otherMethod.getPriority()); // 修更名稱 otherMethod.setName("newName"); log.debug("getName()->{}", otherMethod.getName()); // 獲取全部線程的堆棧信息 Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces(); for (Map.Entry<Thread, StackTraceElement[]> stackTrace : allStackTraces.entrySet()) { Thread thread = (Thread) stackTrace.getKey(); StackTraceElement[] stackTraceElements = stackTrace.getValue(); log.debug(" 線程->" + thread.getName()); for (StackTraceElement s : stackTraceElements) { log.debug(" 線程stackTraceElements->" + s); } } } }
結果:
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getName()->Thread-0
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getPriority()->5
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] threadGroup - > java.lang.ThreadGroup[name=main,maxpri=10]
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getPriority()->9
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] getName()->newName
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程->newName
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.Thread.sleep(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->cn.qlq.thread.two.OtherMethod.run(OtherMethod.java:21)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程->main
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.Thread.dumpThreads(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.Thread.getAllStackTraces(Thread.java:1640)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->cn.qlq.thread.two.OtherMethod.main(OtherMethod.java:45)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程->Attach Listener
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程->Signal Dispatcher
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程->Reference Handler
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.Object.wait(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.Object.wait(Object.java:503)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.ref.Reference$ReferenceHandler.run(Reference.java:133)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程->Finalizer
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.Object.wait(Native Method)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:135)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:151)
2018-12-05 22:37:02 [cn.qlq.thread.two.OtherMethod]-[DEBUG] 線程stackTraceElements->java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:209)
還能夠獲取全部活線程數量:
package cn.qlq.thread.two; public class OtherMethod2 { public static void main(String[] args) { // 構造一個線程並啓動,取名爲"my-thread" Thread myThread = new Thread(new Runnable() { public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }, "my-thread"); myThread.start(); // 返回一個活線程數量 int activeCount = Thread.activeCount(); System.out.println(activeCount); Thread[] ts = new Thread[activeCount]; Thread.enumerate(ts); for (Thread t : ts) { System.out.println("從主線程中找到線程名稱爲:" + t.getName() + ", 狀態爲: " + t.getState()); } } }
結果:
2
從主線程中找到線程名稱爲:main, 狀態爲: RUNNABLE
從主線程中找到線程名稱爲:my-thread, 狀態爲: TIMED_WAITING
補充:線程啓動的時候JVM會建立幾個默認的守護線程。
當咱們啓動一個線程的時候,JVM會自動建立幾個默認的守護線程(這些守護線程並非一直存活,有的線程會執行完畢),這也解釋了上面的線程ID爲何缺失了0、2-8的ID。
例如:
package cn.qlq.thread.two; public class ThreadSleep2SeeJVMThread { public static void main(String[] args) { // 構造一個線程並啓動,取名爲"my-thread" Thread myThread = new Thread(new Runnable() { public void run() { try { Thread.sleep(100 * 10000); } catch (InterruptedException e) { e.printStackTrace(); } } }, "my-thread"); myThread.start(); System.out.println("myThread id ->" + myThread.getId()); try { Thread.sleep(500 * 10000); } catch (InterruptedException e) { e.printStackTrace(); } } }
結果:
myThread id ->9
在上面故意讓主線程和新建的線程進行休眠是爲了更好的查看線程的狀態。如今咱們利用JDK自帶的JVisualVM查看線程:
總共11個線程,守護線程有9個。這也解釋了上面的線程ID爲何缺失了0、2-8的ID。(查看線程的具體狀態以下:)
能夠看到main線程和my-thread是本身手動開啓的線程,在休眠狀態。
剩下的9個守護線程是JVM幫助咱們建立的。解釋其中幾個重要的線程的意義:
關於JVM自動啓動的線程的解釋參考:http://ifeve.com/jvm-thread/