線程類有好多方法,下面介紹一些常用的方法。
1.start() 實例方法
啟動一個線程用的是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()方法會報錯:
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()); } }
結果:
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-0
2. Thread.currentThread靜態方法
/** * 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()); } }
結果:
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 -> main
查看Thread.toString()的源碼返回的是線程的名稱+優先級+所屬組的名稱:
public String toString() { ThreadGroup group = getThreadGroup(); if (group != null) { return "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]"; } else { return "Thread[" + getName() + "," + getPriority() + "," + "" + "]"; } }
3.實例方法 isAlive()
/** * 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休眠之后,會繼續執行休眠之后的代碼。
4.靜態方法 sleep()
/** * 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控制權的一種操作。
5.實例方法 getId()
/** * 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,這也是一直跟代碼沒有跟出來。。。。。。。(此處有疑問-----答案在最后補充中得以證實)
6.Thread.yield()靜態方法沒有參數
/** * 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毫秒
7. join實例方法:
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
8.線程的優先級
在操作系統中,線程可以划分優先級,優先級較高的線程可以獲得更多的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
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執行完所以也正常結束)
10.其他方法
其他還有獲取線程名稱,線程組的方法等方法。
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幫助我們創建的。解釋其中幾個重要的線程的意義:
- Attach Listener :線程是負責接收到外部的命令,而對該命令進行執行的並且吧結果返回給發送者。通常我們會用一些命令去要求jvm給我們一些反饋信息,如:java -version、jmap、jstack等等。如果該線程在jvm啟動的時候沒有初始化,那么,則會在用戶第一次執行jvm命令時,得到啟動。
- signal dispather: 前面我們提到第一個Attach Listener線程的職責是接收外部jvm命令,當命令接收成功后,會交給signal dispather線程去進行分發到各個不同的模塊處理命令,並且返回處理結果。signal dispather線程也是在第一次接收外部jvm命令時,進行初始化工作。
- Finalizer: 用來執行所有用戶Finalizer 方法的線程
- Reference Handler :它主要用於處理引用對象本身(軟引用、弱引用、虛引用)的垃圾回收問題。
關於JVM自動啟動的線程的解釋參考:http://ifeve.com/jvm-thread/